@koda-sl/baker-cli 0.450.0 → 0.451.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/README.md +8 -0
  2. package/dist/{canvas-XYCDLDRS.js → canvas-LSQIZ4YK.js} +12 -12
  3. package/dist/{catalog-ME44ZRZF.js → catalog-BWWRJ5RC.js} +3 -3
  4. package/dist/{chunk-P2RE52V7.js → chunk-CCTB7IA7.js} +3 -3
  5. package/dist/{chunk-D5XAVXUZ.js → chunk-DGICXFK4.js} +5 -5
  6. package/dist/{chunk-542XAHNT.js → chunk-ICGLN6VO.js} +2 -2
  7. package/dist/{chunk-J5IY3GVE.js → chunk-MCVASJR5.js} +3 -3
  8. package/dist/{chunk-GLCPTMPG.js → chunk-PHKQRZGR.js} +2 -2
  9. package/dist/{chunk-K7FZXIOZ.js → chunk-PZ7EPJRF.js} +2 -2
  10. package/dist/{chunk-PLGIETFZ.js → chunk-QDSSRRI2.js} +6 -6
  11. package/dist/{chunk-NEXCBTBZ.js → chunk-QUST75OM.js} +2 -2
  12. package/dist/{chunk-TXI5OWCF.js → chunk-U6GDJF7Y.js} +15 -9
  13. package/dist/{chunk-TXI5OWCF.js.map → chunk-U6GDJF7Y.js.map} +1 -1
  14. package/dist/{chunk-P6ICBEE4.js → chunk-W2KF4LZL.js} +3 -3
  15. package/dist/chunk-W2KF4LZL.js.map +1 -0
  16. package/dist/{chunk-TD6GIMV6.js → chunk-WHIPHGWI.js} +2 -2
  17. package/dist/{chunk-K66OLA6Y.js → chunk-ZIHEIVJC.js} +51 -24
  18. package/dist/chunk-ZIHEIVJC.js.map +1 -0
  19. package/dist/cli.js +2 -2
  20. package/dist/{creatives-D3NXWVUF.js → creatives-2CYEJXYR.js} +5 -5
  21. package/dist/{critique-3XJG44NG.js → critique-GUXOLOES.js} +3 -3
  22. package/dist/engine/index.js +2 -2
  23. package/dist/{feedback-C6CBUUDA.js → feedback-6N2BWUND.js} +2 -2
  24. package/dist/{feedback-record-FM5MW2YV.js → feedback-record-L7YINZLA.js} +5 -5
  25. package/dist/{make-ATEHKECL.js → make-RM2HEOVC.js} +10 -7
  26. package/dist/{make-ATEHKECL.js.map → make-RM2HEOVC.js.map} +1 -1
  27. package/dist/{rerun-FCZRSS3H.js → rerun-2OYJNDXC.js} +10 -10
  28. package/dist/{run-CUUAWKAZ.js → run-CECNWLTF.js} +10 -10
  29. package/dist/{scaffold-ad-AYKWASWY.js → scaffold-ad-WGYLP7HP.js} +7 -7
  30. package/dist/{scaffold-static-ad-IEZEQW4N.js → scaffold-static-ad-Z54SETYF.js} +15 -15
  31. package/dist/scaffold-static-ad-Z54SETYF.js.map +1 -0
  32. package/dist/{scaffold-video-YMN7AJS4.js → scaffold-video-63VDYVAU.js} +7 -7
  33. package/dist/{select-LSS7ZJSB.js → select-BU2KRFOE.js} +5 -5
  34. package/dist/{set-prompt-P3WR2TZX.js → set-prompt-VLLJUQQ3.js} +4 -4
  35. package/dist/{stale-7VJECYYJ.js → stale-T4S42ATQ.js} +4 -4
  36. package/dist/{sync-definition-ARIX5XWM.js → sync-definition-STAFZTLS.js} +7 -7
  37. package/dist/{translate-GGONJG62.js → translate-PL54KGE4.js} +7 -7
  38. package/dist/{validate-FSFELDFZ.js → validate-KZUQKUNF.js} +5 -5
  39. package/package.json +1 -1
  40. package/dist/chunk-K66OLA6Y.js.map +0 -1
  41. package/dist/chunk-P6ICBEE4.js.map +0 -1
  42. package/dist/scaffold-static-ad-IEZEQW4N.js.map +0 -1
  43. /package/dist/{canvas-XYCDLDRS.js.map → canvas-LSQIZ4YK.js.map} +0 -0
  44. /package/dist/{catalog-ME44ZRZF.js.map → catalog-BWWRJ5RC.js.map} +0 -0
  45. /package/dist/{chunk-P2RE52V7.js.map → chunk-CCTB7IA7.js.map} +0 -0
  46. /package/dist/{chunk-D5XAVXUZ.js.map → chunk-DGICXFK4.js.map} +0 -0
  47. /package/dist/{chunk-542XAHNT.js.map → chunk-ICGLN6VO.js.map} +0 -0
  48. /package/dist/{chunk-J5IY3GVE.js.map → chunk-MCVASJR5.js.map} +0 -0
  49. /package/dist/{chunk-GLCPTMPG.js.map → chunk-PHKQRZGR.js.map} +0 -0
  50. /package/dist/{chunk-K7FZXIOZ.js.map → chunk-PZ7EPJRF.js.map} +0 -0
  51. /package/dist/{chunk-PLGIETFZ.js.map → chunk-QDSSRRI2.js.map} +0 -0
  52. /package/dist/{chunk-NEXCBTBZ.js.map → chunk-QUST75OM.js.map} +0 -0
  53. /package/dist/{chunk-TD6GIMV6.js.map → chunk-WHIPHGWI.js.map} +0 -0
  54. /package/dist/{creatives-D3NXWVUF.js.map → creatives-2CYEJXYR.js.map} +0 -0
  55. /package/dist/{critique-3XJG44NG.js.map → critique-GUXOLOES.js.map} +0 -0
  56. /package/dist/{feedback-C6CBUUDA.js.map → feedback-6N2BWUND.js.map} +0 -0
  57. /package/dist/{feedback-record-FM5MW2YV.js.map → feedback-record-L7YINZLA.js.map} +0 -0
  58. /package/dist/{rerun-FCZRSS3H.js.map → rerun-2OYJNDXC.js.map} +0 -0
  59. /package/dist/{run-CUUAWKAZ.js.map → run-CECNWLTF.js.map} +0 -0
  60. /package/dist/{scaffold-ad-AYKWASWY.js.map → scaffold-ad-WGYLP7HP.js.map} +0 -0
  61. /package/dist/{scaffold-video-YMN7AJS4.js.map → scaffold-video-63VDYVAU.js.map} +0 -0
  62. /package/dist/{select-LSS7ZJSB.js.map → select-BU2KRFOE.js.map} +0 -0
  63. /package/dist/{set-prompt-P3WR2TZX.js.map → set-prompt-VLLJUQQ3.js.map} +0 -0
  64. /package/dist/{stale-7VJECYYJ.js.map → stale-T4S42ATQ.js.map} +0 -0
  65. /package/dist/{sync-definition-ARIX5XWM.js.map → sync-definition-STAFZTLS.js.map} +0 -0
  66. /package/dist/{translate-GGONJG62.js.map → translate-PL54KGE4.js.map} +0 -0
  67. /package/dist/{validate-FSFELDFZ.js.map → validate-KZUQKUNF.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/language.ts","../src/engine/scaffold/static-series.ts","../src/engine/scaffold/static-render-briefs.ts","../src/engine/scaffold/static-adapt-briefs.ts","../src/engine/lib/image-model-router.ts","../src/engine/scaffold/staticAd.ts","../src/engine/scaffold/castAvatar.ts","../src/engine/scaffold/ad-brand-source.ts","../src/engine/scaffold/static-fonts.ts","../src/engine/scaffold/static-brand.ts","../src/engine/scaffold/static-native-graph.ts","../src/commands/canvas/static-copy-projection.ts","../src/engine/scaffold/static-named-people.ts","../src/engine/scaffold/static-proof.ts","../src/engine/scaffold/video/aspect.ts","../src/engine/scaffold/video/elements.ts","../src/engine/scaffold/video/ids.ts","../src/engine/scaffold/video/routing.ts","../src/engine/scaffold/video/overlay-html.ts","../src/engine/scaffold/lib/html.ts","../src/engine/scaffold/video/timing.ts","../src/engine/scaffold/video/projections.ts","../src/engine/scaffold/video/report.ts","../src/engine/scaffold/lib/language.ts","../src/engine/scaffold/video/assembly.ts","../src/engine/scaffold/video/audio.ts","../src/engine/scaffold/video/guide.ts","../src/engine/scaffold/video/meta.ts","../src/engine/scaffold/lib/scene-role.ts","../src/engine/scaffold/video/routes/surface-still.ts","../src/engine/scaffold/video/routes/graphic-plate.ts","../src/engine/scaffold/video/dialogue/speakers.ts","../src/engine/scaffold/video/dialogue/phrases.ts","../src/engine/scaffold/video/ffmpeg-args.ts","../src/engine/scaffold/video/dialogue/voices.ts","../src/engine/scaffold/video/routes/brand-card.ts","../src/engine/scaffold/lib/shoot-modes.ts","../src/engine/scaffold/video/prompts/frame.ts","../src/engine/scaffold/video/frames.ts","../src/engine/scaffold/video/model-params.ts","../src/engine/scaffold/video/prompts/clip.ts","../src/engine/scaffold/video/dialogue/numbers.ts","../src/engine/scaffold/video/routes/clip.ts","../src/engine/scaffold/video/timeline-env.ts","../src/engine/scaffold/video/routes/composite.ts","../src/engine/scaffold/video/routes/phrase-slice.ts","../src/engine/scaffold/video/routes/screen-still.ts","../src/engine/scaffold/video/routes/still-hold.ts","../src/engine/scaffold/video/timeline.ts","../src/engine/scaffold/video/todo.ts","../src/engine/scaffold/video.ts","../src/engine/nodes/local/lib/caption-groups.ts","../src/engine/scaffold/video/speech-reading.ts","../src/commands/canvas/scene-files.ts","../src/commands/canvas/scene-projection.ts","../src/commands/canvas/source-version.ts","../src/engine/nodes/local/lib/jev-cache.ts","../src/engine/scaffold/video/dialogue/voice-gender.ts","../src/commands/canvas/style-projection.ts","../src/commands/canvas/prepare-canvas.ts","../src/commands/canvas/placeholders.ts","../src/commands/canvas/interface-words.ts","../src/commands/canvas/claim-check.ts","../src/engine/scaffold/ad-claims.ts","../src/commands/canvas/native-projection.ts","../src/commands/canvas/promote-canvas-outputs.ts","../src/engine/scaffold/static-upgrade.ts"],"sourcesContent":["/**\n * A language an ad is written in, as a BCP-47 tag: parsed with `Intl`, never by hand. The tag is\n * stored canonical (`es-MX`, `pt-BR`), as ad specs hold it, and compared without case; only where\n * it names something, the translation's creative (`<ad>-es-mx`), is it lowercase.\n */\n\nconst NAMES = new Intl.DisplayNames([\"en\"], { type: \"language\", fallback: \"none\" });\n\n/** The canonical tag of `code` (\"pt-br\" → \"pt-BR\"); null when it names no language. Pure. */\nexport function languageTag(code: string): string | null {\n try {\n const [canonical] = Intl.getCanonicalLocales(code.trim());\n if (!canonical || NAMES.of(canonical) === undefined) return null;\n return canonical;\n } catch {\n return null;\n }\n}\n\n/** Whether two tags name the same language and country (\"es-mx\" and \"es-MX\" do; \"es\" and \"es-MX\" do not). Pure. */\nexport function sameTag(a: string, b: string): boolean {\n return a.toLowerCase() === b.toLowerCase();\n}\n\n/** The tag as it names a creative: lowercase (\"es-MX\" → \"es-mx\"). Pure. */\nexport function tagSlug(tag: string): string {\n return tag.toLowerCase();\n}\n\n/** A language's English name (\"es\" → \"Spanish\", \"pt-br\" → \"Brazilian Portuguese\"); null for a code that names none. Pure. */\nexport function languageName(code: string): string | null {\n try {\n const [canonical] = Intl.getCanonicalLocales(code.trim());\n return canonical ? (NAMES.of(canonical) ?? null) : null;\n } catch {\n return null;\n }\n}\n\nconst REGIONS = new Intl.DisplayNames([\"en\"], { type: \"region\", fallback: \"none\" });\n\n/** The country a tag names (\"es-MX\" → { code: \"MX\", name: \"Mexico\" }); null for a tag with none. Pure. */\nexport function languageRegion(code: string): { code: string; name: string } | null {\n try {\n const region = new Intl.Locale(code.trim()).region;\n const name = region ? REGIONS.of(region) : undefined;\n return region && name ? { code: region, name } : null;\n } catch {\n return null;\n }\n}\n","/**\n * A series is one finished ad repeated with other words: the lead ad is designed once, and every\n * other ad of the series is the lead's picture with only its lines changed (`seriesMemberAd`).\n * Here: which of the lead's lines each of this ad's lines replaces, said as the edit's instruction.\n *\n * Pure.\n */\nimport { languageName } from \"../../language.ts\";\n\ntype Line = { text: string; role?: string; position?: string };\n\n/**\n * The edit that turns the lead's words into this ad's, line by line in the lead's order: a line of\n * this ad replaces the lead's line of the same role (else the next one left, in order, so the\n * layout stays the lead's), a line both share is kept, a lead line with nothing to replace it is\n * removed, and a line of this ad left over is added. The model edits one line in place far better\n * than it re-sets a whole block of type.\n */\nexport function seriesWordsInstruction(\n leadCopy: readonly Line[],\n copy: readonly Line[],\n opts: { language?: string } = {},\n): string {\n const pairs = pairLines(leadCopy, copy);\n const said = leadCopy.map((from, i) => {\n const to = pairs.get(i);\n if (!to) return `Remove \"${from.text}\" and give its space to the background.`;\n return to.text === from.text ? `Keep \"${from.text}\" as it is.` : `Replace \"${from.text}\" with \"${to.text}\"`;\n });\n const paired = new Set(pairs.values());\n const added = copy\n .filter((line) => !paired.has(line))\n .map(\n (line) =>\n `Add \"${line.text}\"${line.position ? ` ${line.position}` : \"\"}, in the lettering of the ad's other lines.`,\n );\n const language = opts.language ? languageName(opts.language) : null;\n const lettering = language ? [`The new words are in ${language}: set every accent and mark exactly as written`] : [];\n return [...lettering, ...said, ...added].map(endSentence).join(\" \");\n}\n\n/** Each lead line's replacement, by index: the same words, else the same role, else the next line left, in order. */\nfunction pairLines(leadCopy: readonly Line[], copy: readonly Line[]): Map<number, Line> {\n const pairs = new Map<number, Line>();\n const free = [...copy];\n const take = (match: (line: Line) => boolean): Line | undefined => {\n const at = free.findIndex(match);\n return at < 0 ? undefined : free.splice(at, 1)[0];\n };\n leadCopy.forEach((from, i) => {\n const same =\n take((line) => line.text === from.text) ?? (from.role ? take((line) => line.role === from.role) : undefined);\n if (same) pairs.set(i, same);\n });\n leadCopy.forEach((_from, i) => {\n if (pairs.has(i)) return;\n const next = take(() => true);\n if (next) pairs.set(i, next);\n });\n return pairs;\n}\n\nfunction endSentence(text: string): string {\n return /[.!?]\"?$/.test(text) ? text : `${text}.`;\n}\n","/**\n * A static ad canvas's render prompts, compiled from its `prompt.json`.\n *\n * Every render node of a static ad (the candidate takes, the re-lays, the judge) carries the\n * brief `static-brief.ts` compiles from the ad spec, never the spec itself: the model, the\n * cache key and the dashboard all see the prompt that renders. The scaffold writes them and\n * `prepareCanvas` projects them again on every run (`syncStaticAdBrief`), so an edit to\n * prompt.json reaches exactly the nodes it changes. A canvas scaffolded before the compiler\n * (its prompt inlining the blueprint, the source ad among its references) is upgraded in place\n * by the same projection on its next run.\n *\n * Pure: canvas + prompt.json content in, nodes rewritten in place.\n */\n\nimport { GPT_IMAGE_25_PROFILE, imageProfileFor } from \"../lib/prompt-profiles.ts\";\nimport { type StaticChecks, staticChecksOf } from \"../nodes/local/lib/variant-prefilter.ts\";\nimport {\n type AdReference,\n type AdSpec,\n readAdSpec,\n referenceNodeId,\n seesReferenceStructure,\n withRenderedLettering,\n} from \"./static-ad-spec.ts\";\nimport { adaptPlan, trimmedRenderBand } from \"./static-adapt.ts\";\nimport { type BriefImages, type BriefJob, compileStaticBrief, judgeBrief, type PlannedBand } from \"./static-brief.ts\";\nimport { COPY_DIRECTIVE_HEADER, replaceCopyDirective, specimenText, textFidelityDirective } from \"./static-copy.ts\";\nimport { seriesWordsInstruction } from \"./static-series.ts\";\nimport { MASTER_SHAPES, type MasterShape, planStaticFormats } from \"./static-sizes.ts\";\nimport {\n SERIES_LEAD_ROLE,\n STATIC_ADAPT_ROLE,\n STATIC_VARIANT_CALL_ROLE,\n STATIC_VARIANTS_ROLE,\n staticMasterFormat,\n VARIANT_PREFILTER_ROLE,\n} from \"./static-topology.ts\";\n\n/** The tag on the `prompt` ingest that says this canvas is a static ad (tags never enter the cache key). */\nexport const STATIC_AD_ROLE = \"static_ad_blueprint\";\n/** The tag on the ingest of the brand's real logo file, whatever id the element pass gave it. */\nexport const BRAND_LOGO_ROLE = \"brand_logo\";\n/** The tag a pre-topology canvas put on one of its takes (upgraded by `upgradeStaticCanvas`). */\nexport const STATIC_CANDIDATE_ROLE = \"static_candidate\";\n/** The tag a pre-topology canvas put on a re-lay of the chosen ad (upgraded by `upgradeStaticCanvas`). */\nexport const STATIC_RELAY_ROLE = \"static_relay\";\n\nexport type StaticCanvasNode = {\n id: string;\n type: string;\n params?: Record<string, unknown>;\n inputs?: Record<string, unknown>;\n tags?: { role?: string; region?: number };\n};\n\nfunction nodesOf(canvas: unknown): StaticCanvasNode[] {\n const nodes = (canvas as { nodes?: unknown } | null)?.nodes;\n return Array.isArray(nodes) ? (nodes as StaticCanvasNode[]) : [];\n}\n\nconst refId = (ref: unknown): string | undefined =>\n typeof ref === \"string\" ? ref.match(/^\\$ref:([a-z][a-z0-9_]*)\\./)?.[1] : undefined;\n\n/** Whether a canvas is a static ad whose renders compile from prompt.json. */\nexport function isStaticBriefCanvas(canvas: unknown): boolean {\n return isStaticCanvas(nodesOf(canvas));\n}\n\nfunction isStaticCanvas(nodes: StaticCanvasNode[]): boolean {\n if (nodes.some((n) => n.id === \"prompt\" && n.type === \"ingest\" && n.tags?.role === STATIC_AD_ROLE)) return true;\n // Scaffolded before the prompt ingest was tagged: its renders carry the copy block.\n return nodes.some(\n (n) =>\n n.type === \"image_generate\" &&\n typeof n.params?.prompt === \"string\" &&\n n.params.prompt.includes(COPY_DIRECTIVE_HEADER),\n );\n}\n\ntype Render = { node: StaticCanvasNode; job: Exclude<BriefJob, \"fix\">; variant: number; format: string };\n\n/** The lead ad a series ad replaces the words of (`metadata.series`, written by `seriesMemberAd`); null on any other ad. */\nexport type SeriesOf = {\n lead: string;\n run_id: string;\n copy: Array<{ text: string; role?: string }>;\n /** A translation of the lead: the language its words are in. */\n language?: string;\n};\n\n/** The language a translation's words are in, for `seriesWordsInstruction`. */\nexport function wordsLanguage(series: SeriesOf): { language?: string } {\n return typeof series.language === \"string\" ? { language: series.language } : {};\n}\n\nexport function seriesOf(canvas: unknown): SeriesOf | null {\n const series = (canvas as { metadata?: { series?: unknown } } | null)?.metadata?.series as\n | Partial<SeriesOf>\n | undefined;\n return series && typeof series.lead === \"string\" && Array.isArray(series.copy) ? (series as SeriesOf) : null;\n}\n\n/** The job one node does, read off its tag (or, on an older canvas, its id); null when it is not a static render. */\nfunction renderOf(node: StaticCanvasNode, lead: string | undefined): Render | null {\n if (node.type !== \"image_generate\") return null;\n return variantRenderOf(node, lead) ?? legacyRenderOf(node, lead);\n}\n\n/** The variants: one request each on its own direction, or (a single variant, or one `n` request on an older canvas) variant 0's. */\nfunction variantRenderOf(node: StaticCanvasNode, lead: string | undefined): Render | null {\n if (node.tags?.role === STATIC_VARIANTS_ROLE) return { node, job: \"generate\", variant: 0, format: lead ?? \"9:16\" };\n if (node.tags?.role !== STATIC_VARIANT_CALL_ROLE) return null;\n return { node, job: \"generate\", variant: node.tags.region ?? 0, format: lead ?? \"9:16\" };\n}\n\n/** A take or a re-lay of a canvas scaffolded before the one-picture graph (`upgradeStaticCanvas` replaces them). */\nfunction legacyRenderOf(node: StaticCanvasNode, lead: string | undefined): Render | null {\n const take = node.id.match(/^ad(?:_([a-c]))?$/);\n if (node.tags?.role === STATIC_CANDIDATE_ROLE || take) {\n const letter = take?.[1];\n const variant = node.tags?.region ?? (letter ? letter.charCodeAt(0) - 97 : 0);\n return { node, job: \"generate\", variant, format: lead ?? String(node.params?.aspect_ratio ?? \"9:16\") };\n }\n const relay = node.id.match(/^ad_(\\d+)x(\\d+)$/);\n if (node.tags?.role !== STATIC_RELAY_ROLE && !relay) return null;\n const format = relay ? `${relay[1]}:${relay[2]}` : String(node.params?.aspect_ratio ?? \"4:5\");\n return { node, job: \"relay\", variant: 0, format };\n}\n\n/** The render nodes, with the job each does. */\nfunction staticRenders(nodes: StaticCanvasNode[]): Render[] {\n const lead = renderFormat(nodes);\n return nodes.flatMap((node) => {\n const render = renderOf(node, lead);\n return render ? [render] : [];\n });\n}\n\n/** The format the ad is rendered for: the master's, else (an older canvas) the first delivered format. */\nfunction renderFormat(nodes: StaticCanvasNode[]): string | undefined {\n const master = staticMasterFormat({ nodes });\n if (master) return master;\n const labels = nodes.find((n) => n.id === \"final\" && n.type === \"collect\")?.params?.labels;\n return Array.isArray(labels) && typeof labels[0] === \"string\" ? labels[0] : undefined;\n}\n\n/** The ad set's placements the canvas's sizes were planned with (kept on the prefilter), for the brief's safe area. */\nfunction plannedPlacements(nodes: StaticCanvasNode[]): { placements?: string[] } {\n const placements = nodes.find((n) => n.tags?.role === VARIANT_PREFILTER_ROLE)?.params?.placements;\n return Array.isArray(placements) && placements.every((p) => typeof p === \"string\") ? { placements } : {};\n}\n\n/**\n * The content band the canvas's sizes were planned with (`planStaticFormats` over the formats,\n * master shape and placements kept on the prefilter), when `format` is its master: the one\n * region a render at the master must keep every word in, so every cut keeps it too.\n */\nfunction plannedBand(nodes: StaticCanvasNode[], format: string, model: string): { band?: PlannedBand } {\n const params = nodes.find((n) => n.tags?.role === VARIANT_PREFILTER_ROLE)?.params;\n const formats = params?.formats;\n if (!Array.isArray(formats) || formats.length === 0 || !formats.every((f) => typeof f === \"string\")) return {};\n const shape = MASTER_SHAPES.find((s) => s === params?.master_shape);\n const plan = planStaticFormats(model, formats, {\n ...(shape ? { masterShape: shape as MasterShape } : {}),\n ...plannedPlacements(nodes),\n });\n if (plan.master.format !== format) return {};\n // Re-laid out, no size is cut from the master: its own safe area holds, widened by whatever\n // the fill to its format trims off the render (GPT renders a 4:5 at 3:4).\n if (plan.masterShape === \"relayout\") {\n const band = trimmedRenderBand(adaptPlan(model, format, plan.placements));\n return band ? { band } : {};\n }\n return { band: { area: plan.band, cut: plan.sizes.some((s) => s.from === \"crop\") } };\n}\n\n/** An element slot the spec does not name, given a role from what its node is. */\nfunction unnamedReference(id: string, taken: AdReference[]): AdReference {\n const [prefix, role] = /person|avatar/.test(id)\n ? ([\"PERSON\", \"identity\"] as const)\n : /_sheet$|animal|dog|cat|pet|hero/.test(id)\n ? ([\"ANIMAL\", \"identity\"] as const)\n : /product|package|bottle|box/.test(id)\n ? ([\"PRODUCT\", \"product\"] as const)\n : ([\"PROP\", \"prop\"] as const);\n let n = 1;\n while (taken.some((r) => r.id === `${prefix}_${n}`)) n++;\n return {\n id: `${prefix}_${n}`,\n role,\n slot: id.replace(/_sheet$/, \"\"),\n what: `the ${role === \"identity\" ? \"subject\" : role === \"product\" ? \"product\" : \"object\"} in this picture`,\n };\n}\n\n/**\n * The spec reference an element slot fills: the one naming its slot, else one the spec describes\n * without a picture (a blueprint's product) of the same kind, else a new role-based one. Added to\n * `references` (in place) when it is not there yet.\n */\nfunction referenceFor(id: string, references: AdReference[]): AdReference {\n const slot = id.replace(/_sheet$/, \"\");\n const named = references.find((r) => r.slot === slot || r.slot === id || referenceNodeId(r) === id);\n if (named) return named;\n const fresh = unnamedReference(id, references);\n const kind = (r: AdReference) => r.id.split(\"_\")[0];\n const waiting = references.findIndex((r) => !r.slot && r.role === fresh.role && kind(r) === kind(fresh));\n if (waiting < 0) {\n references.push(fresh);\n return fresh;\n }\n const adopted = { ...(references[waiting] as AdReference), slot };\n references[waiting] = adopted;\n return adopted;\n}\n\n/**\n * What each picture wired into a render is, for the compiler: the ad being re-laid or\n * corrected, the logo, the type specimen, the source ad, or the spec reference whose slot it\n * is (an element the spec does not name gets a role-based one). Returns the spec with those\n * additions and the pictures keyed as the compiler reads them.\n */\nexport function bindBriefImages(\n nodes: StaticCanvasNode[],\n reference: unknown,\n spec: AdSpec,\n): { spec: AdSpec; images: BriefImages } {\n const byId = new Map(nodes.map((n) => [n.id, n] as const));\n const refs: Record<string, string> = {};\n const images: BriefImages = { refs };\n const references = [...spec.references];\n const list = Array.isArray(reference) ? reference : reference === undefined ? [] : [reference];\n for (const ref of list) {\n const id = refId(ref);\n if (!id || typeof ref !== \"string\") continue;\n const kind = pictureKind(id, byId.get(id));\n if (kind !== \"element\") {\n images[kind] = ref;\n continue;\n }\n refs[referenceFor(id, references).id] = ref;\n }\n return { spec: { ...spec, references }, images };\n}\n\n/** What a wired picture is, by the node it comes from. */\nfunction pictureKind(\n id: string,\n target: StaticCanvasNode | undefined,\n): \"layout\" | \"base\" | \"logo\" | \"type\" | \"element\" {\n if (id === \"original\") return \"layout\";\n // A picture made in the graph: a render, a pick, or the variant the checks framed (a grown version's).\n const made = [\"image_generate\", \"image_select\", \"static_prefilter\", \"collect\"];\n if (id === \"previous\" || made.includes(String(target?.type))) return \"base\";\n if (target?.tags?.role === SERIES_LEAD_ROLE) return \"base\";\n if (target?.tags?.role === BRAND_LOGO_ROLE) return \"logo\";\n return target?.type === \"font_specimen\" ? \"type\" : \"element\";\n}\n\n/** Rewrite one render node to its compiled brief. Returns whether anything changed. */\nfunction applyBrief(node: StaticCanvasNode, prompt: string, reference: string[]): boolean {\n const before = JSON.stringify([node.params?.prompt, node.inputs]);\n node.params = { ...node.params, prompt };\n const inputs: Record<string, unknown> = { ...node.inputs };\n delete inputs.blueprint;\n if (reference.length > 0) inputs.reference = reference;\n else delete inputs.reference;\n node.inputs = inputs;\n return JSON.stringify([node.params.prompt, node.inputs]) !== before;\n}\n\nconst profileOf = (node: StaticCanvasNode) => imageProfileFor(String(node.params?.model ?? \"\")) ?? GPT_IMAGE_25_PROFILE;\n\n/**\n * Built on the reference's concept, a first render sees the reference for its structure (C5) when\n * what won is the whole ad or its layout (`seesReferenceStructure`).\n */\nfunction structureRef(nodes: StaticCanvasNode[], spec: AdSpec): string | undefined {\n return seesReferenceStructure(spec) && nodes.some((n) => n.id === \"original\" && n.type === \"ingest\")\n ? \"$ref:original.asset\"\n : undefined;\n}\n\n/** The pictures a render is wired to, the reference's structure added to a first render. */\nfunction referencesOf(render: { node: StaticCanvasNode; job: string }, structure: string | undefined): unknown[] {\n const wired = render.node.inputs?.reference;\n const list: unknown[] = Array.isArray(wired) ? wired : wired === undefined ? [] : [wired];\n return render.job === \"generate\" && structure && !list.includes(structure) ? [...list, structure] : list;\n}\n\n/**\n * Compile every render of a static canvas from its prompt.json, in place: each take, each\n * re-lay, the judge's criteria and the type specimen's text. Returns how many nodes changed\n * (0 for a canvas that is not a static ad, and for one already up to date). Throws when an\n * ad spec in prompt.json is invalid: the caller stops before anything bills.\n */\nexport function projectStaticBriefs(canvas: unknown, promptJson: unknown): number {\n const nodes = nodesOf(canvas);\n if (!isStaticCanvas(nodes)) return 0;\n const spec = readAdSpec(promptJson);\n const series = seriesOf(canvas);\n // An ad of a series renders the lead's picture with its words replaced: its variant is that edit.\n const renders = staticRenders(nodes).map((r) =>\n series && r.job === \"generate\" ? { ...r, job: \"series\" as const } : r,\n );\n let changed = 0;\n let judgeFrom: string | undefined;\n const structure = structureRef(nodes, spec);\n for (const render of renders) {\n const bound = bindBriefImages(nodes, referencesOf(render, structure), spec);\n const profile = profileOf(render.node);\n const compile = (variant: number) =>\n compileStaticBrief(bound.spec, undefined, render.format, undefined, profile, {\n job: render.job,\n variant,\n ...(series && render.job === \"series\"\n ? { instruction: seriesWordsInstruction(series.copy, spec.copy, wordsLanguage(series)) }\n : {}),\n images: bound.images,\n ...plannedPlacements(nodes),\n ...(render.job === \"generate\"\n ? plannedBand(nodes, render.format, String(render.node.params?.model ?? \"\"))\n : {}),\n });\n const brief = compile(render.variant);\n if (\n applyBrief(\n render.node,\n brief.prompt,\n brief.references.map((r) => r.ref),\n )\n )\n changed++;\n if (render.job === \"generate\") judgeFrom ??= compile(0).prompt;\n }\n if (judgeFrom) changed += projectJudges(nodes, spec, judgeFrom);\n changed += projectHandWrittenCopy(nodes, spec, new Set(renders.map((r) => r.node)));\n return changed + projectSpecimens(nodes, spec) + projectAdaptCopy(nodes, spec) + projectPlacementCopy(nodes, spec);\n}\n\n/**\n * The steps that place a size clear of an interface (`static_safe_fit`, `static_place`) are told\n * the ad's copy and the role of each line, and the brand's name and the words its logo prints\n * (role `logo`), so they move the ad for its own words and logo, never for what the scene prints\n * (`must-show.ts`). Returns how many nodes changed.\n */\nexport function projectPlacementCopy(nodes: StaticCanvasNode[], spec: Pick<AdSpec, \"copy\" | \"brand\">): number {\n // The brand's name and the words its logo prints count as the logo, as the review counts them.\n const logoWords = [...new Set([...(spec.brand?.name ? [spec.brand.name] : []), ...(spec.brand?.lockup ?? [])])];\n const copy = [\n ...spec.copy.map((l) => ({ text: l.text, ...(l.role ? { role: l.role } : {}) })),\n ...logoWords.map((text) => ({ text, role: \"logo\" })),\n ];\n let changed = 0;\n for (const step of nodes.filter((n) => n.type === \"static_safe_fit\" || n.type === \"static_place\")) {\n const before = JSON.stringify(step.params?.copy);\n const params: Record<string, unknown> = { ...step.params };\n if (copy.length > 0) params.copy = copy;\n else delete params.copy;\n step.params = params;\n if (JSON.stringify(params.copy) !== before) changed++;\n }\n return changed;\n}\n\n/**\n * The reframe of the master is told the exact words it must keep, each once, so a size it\n * adapts cannot drift from the master's copy (the model reads them off the picture otherwise).\n */\nfunction projectAdaptCopy(nodes: StaticCanvasNode[], spec: AdSpec): number {\n const copy = spec.copy.map((l) => l.text);\n let changed = 0;\n for (const adapt of nodes.filter((n) => n.type === \"image_aspect_adapt\" && n.tags?.role === STATIC_ADAPT_ROLE)) {\n const before = JSON.stringify(adapt.params?.copy);\n const params: Record<string, unknown> = { ...adapt.params };\n if (copy.length > 0) params.copy = copy;\n else delete params.copy;\n adapt.params = params;\n if (JSON.stringify(params.copy) !== before) changed++;\n }\n return changed;\n}\n\n/** What the variant checks read of the spec. A logo is checked for only when its file is wired in: without one none is drawn (never a stand-in). */\nfunction variantChecks(nodes: StaticCanvasNode[], spec: AdSpec): StaticChecks | null {\n const checks = staticChecksOf(spec);\n return checks ? { ...checks, logo: checks.logo && nodes.some((n) => n.tags?.role === BRAND_LOGO_ROLE) } : null;\n}\n\n/** The judge's criteria (on the prefilter, or an older graph's judge node): the brief the takes render from, the brand and the viewer; no temperature. */\nfunction projectJudges(nodes: StaticCanvasNode[], spec: AdSpec, brief: string): number {\n const criteria = judgeBrief(brief, {\n ...(spec.brand?.name ? { brand: spec.brand.name } : {}),\n ...(spec.viewer?.who ? { viewer: spec.viewer.who } : {}),\n mechanisms: spec.mechanism.map((m) => m.device),\n });\n let changed = 0;\n // The prefilter asks the judge on a tie (the current graph has no judge node), and reads only\n // what its checks need of prompt.json, so a note-only edit changes no step (E3).\n const checks = variantChecks(nodes, spec);\n for (const prefilter of nodes.filter((n) => n.tags?.role === VARIANT_PREFILTER_ROLE)) {\n const before = JSON.stringify([prefilter.params, prefilter.inputs]);\n const params: Record<string, unknown> = { ...prefilter.params, ...(checks ? { checks } : {}) };\n if (params.judge_model) params.judge_prompt = criteria;\n prefilter.params = params;\n if (checks && prefilter.inputs && \"spec\" in prefilter.inputs) {\n const { spec: _spec, ...rest } = prefilter.inputs;\n prefilter.inputs = rest;\n }\n if (JSON.stringify([prefilter.params, prefilter.inputs]) !== before) changed++;\n }\n for (const pick of nodes.filter((n) => n.type === \"image_select\")) {\n const before = JSON.stringify([pick.params, pick.inputs]);\n const params: Record<string, unknown> = { ...pick.params, prompt: criteria };\n // Gemini 3 wants its default temperature (prompting-google-gemini-image.md §7).\n delete params.temperature;\n pick.params = params;\n const inputs: Record<string, unknown> = { ...pick.inputs };\n delete inputs.blueprint;\n pick.inputs = inputs;\n if (JSON.stringify([pick.params, pick.inputs]) !== before) changed++;\n }\n return changed;\n}\n\n/**\n * A render written by hand (not one of the scaffold's) keeps its own prompt; only its copy\n * block is re-read from prompt.json, as it always was.\n */\nfunction projectHandWrittenCopy(nodes: StaticCanvasNode[], spec: AdSpec, compiled: Set<StaticCanvasNode>): number {\n const directive = textFidelityDirective(\n spec.copy.map((l) => l.text),\n nodes.some((n) => n.type === \"font_specimen\"),\n );\n let changed = 0;\n for (const node of nodes) {\n const current = node.params?.prompt;\n if (node.type !== \"image_generate\" || compiled.has(node) || typeof current !== \"string\") continue;\n const next = replaceCopyDirective(current, directive);\n if (next === current) continue;\n node.params = { ...node.params, prompt: next };\n changed++;\n }\n return changed;\n}\n\n/** The type specimen shows the lines set in the brand's face (every line when none is). */\nfunction projectSpecimens(nodes: StaticCanvasNode[], spec: AdSpec): number {\n const designed = withRenderedLettering(spec)\n .copy.filter((l) => l.lettering === \"designed\")\n .map((l) => l.text);\n const text = specimenText(designed.length > 0 ? designed : spec.copy.map((l) => l.text));\n let changed = 0;\n for (const specimen of nodes.filter((n) => n.type === \"font_specimen\")) {\n const current = specimen.params?.text;\n if ((typeof current === \"string\" && current.includes(\"{{\")) || current === text) continue;\n specimen.params = { ...specimen.params, text };\n changed++;\n }\n return changed;\n}\n\n/**\n * The ids of the nodes whose prompt is compiled from prompt.json on every run (the takes,\n * the re-lays, the judge): a prompt set on one of them by hand would be overwritten.\n */\nexport function compiledPromptNodeIds(canvas: unknown): Set<string> {\n const nodes = nodesOf(canvas);\n if (!isStaticCanvas(nodes)) return new Set();\n return new Set([\n ...staticRenders(nodes).map((r) => r.node.id),\n ...nodes.filter((n) => n.type === \"image_select\").map((n) => n.id),\n ]);\n}\n\n/**\n * The correction render that replaces the takes on `--fix`: the template take's model and\n * params, `previous` first among its pictures (the source ad dropped), and a brief that\n * changes only what the instruction says.\n */\nexport function fixBriefNode(\n canvas: unknown,\n template: StaticCanvasNode,\n promptJson: unknown,\n opts: { instruction: string; previous: string; format?: string; drop?: ReadonlySet<string> },\n): StaticCanvasNode {\n const nodes = nodesOf(canvas);\n const spec = readAdSpec(promptJson);\n const templateRefs = template.inputs?.reference;\n // A series ad is corrected from its own picture: the lead's it was made from is no longer one to copy.\n const leads = new Set(nodes.filter((n) => n.tags?.role === SERIES_LEAD_ROLE).map((n) => n.id));\n const kept = (Array.isArray(templateRefs) ? templateRefs : []).filter(\n (r): r is string =>\n typeof r === \"string\" && refId(r) !== \"original\" && !opts.drop?.has(refId(r) ?? \"\") && !leads.has(refId(r) ?? \"\"),\n );\n const bound = bindBriefImages(nodes, [opts.previous, ...kept], spec);\n const format = opts.format ?? renderFormat(nodes) ?? \"9:16\";\n const brief = compileStaticBrief(bound.spec, undefined, format, undefined, profileOf(template), {\n job: \"fix\",\n instruction: opts.instruction,\n images: bound.images,\n ...plannedPlacements(nodes),\n ...plannedBand(nodes, format, String(template.params?.model ?? \"\")),\n });\n const inputs: Record<string, unknown> = { ...template.inputs, reference: brief.references.map((r) => r.ref) };\n delete inputs.blueprint;\n return { ...template, id: \"ad\", inputs, params: { ...template.params, prompt: brief.prompt } };\n}\n","/**\n * The briefs of a static ad's re-laid-out sizes (`static_adapt_format`), compiled from\n * prompt.json on every run, the way `projectStaticBriefs` compiles the variants': each size\n * is handed the chosen ad, the logo file the model draws and the brief for its own frame\n * (`adaptBrief`). Its own module so the render graph (`static-topology.ts`) can read the\n * adapt plan without importing the brief projection.\n */\nimport { imageProfileFor } from \"../lib/prompt-profiles.ts\";\nimport { type AdSpec, readAdSpec } from \"./static-ad-spec.ts\";\nimport { adaptBrief, adaptPlan, STATIC_ADAPT_FORMAT_ROLE } from \"./static-adapt.ts\";\nimport { compileStaticBrief } from \"./static-brief.ts\";\nimport {\n BRAND_LOGO_ROLE,\n bindBriefImages,\n type SeriesOf,\n type StaticCanvasNode,\n seriesOf,\n wordsLanguage,\n} from \"./static-render-briefs.ts\";\nimport { seriesWordsInstruction } from \"./static-series.ts\";\nimport {\n formatSlug,\n LOGO_COMPOSITE_ROLE,\n SERIES_LEAD_ROLE,\n STATIC_VARIANT_CALL_ROLE,\n STATIC_VARIANTS_ROLE,\n} from \"./static-topology.ts\";\n\ntype Node = {\n id?: unknown;\n type?: unknown;\n params?: Record<string, unknown>;\n inputs?: Record<string, unknown>;\n tags?: { role?: unknown };\n};\n\n/** `adapt_9x16` → `9:16`, `adapt_1_91x1` → `1.91:1`: the format a re-lay node's id names. */\nexport function formatOfAdaptId(id: string): string | null {\n const m = id.match(/^adapt_(\\d+(?:_\\d+)?)x(\\d+(?:_\\d+)?)$/);\n return m ? `${m[1]?.replace(\"_\", \".\")}:${m[2]?.replace(\"_\", \".\")}` : null;\n}\n\n/** The re-lay nodes of a canvas, each with the format it delivers. */\nexport function adaptNodes(canvas: unknown): Array<{ node: Node & { id: string }; format: string }> {\n const nodes = (canvas as { nodes?: unknown } | null)?.nodes;\n if (!Array.isArray(nodes)) return [];\n return (nodes as Node[]).flatMap((node) => {\n if (node?.type !== \"image_generate\" || node.tags?.role !== STATIC_ADAPT_FORMAT_ROLE || typeof node.id !== \"string\")\n return [];\n const format = formatOfAdaptId(node.id);\n return format ? [{ node: node as Node & { id: string }, format }] : [];\n });\n}\n\n/**\n * Compile each re-laid-out size's brief from prompt.json, in place: the chosen ad first, the\n * logo file the model draws (when the spec shows a logo), the format's own frame and safe\n * area for the canvas's planned placements. Returns how many nodes changed.\n */\nexport function projectAdaptBriefs(canvas: unknown, promptJson: unknown): number {\n const adapts = adaptNodes(canvas);\n if (adapts.length === 0) return 0;\n const nodes = (canvas as { nodes: Node[] }).nodes;\n const { spec, refs } = ownPictures(nodes, readAdSpec(promptJson));\n const logo = drawnLogo(nodes);\n const placements = plannedPlacements(nodes);\n const series = seriesOf(canvas);\n let changed = 0;\n for (const { node, format } of adapts) {\n const model = String(node.params?.model ?? \"\");\n const profile = imageProfileFor(model);\n if (!profile) continue;\n const leadSize = series ? leadSizeRef(nodes, format) : undefined;\n const brief =\n series && leadSize\n ? seriesSizeBrief(spec, profile, { series, format, base: leadSize, logo, placements })\n : relayBrief(spec, profile, { model, format, placements, logo, refs });\n const reference = brief.references.map((r) => r.ref);\n if (node.params?.prompt === brief.prompt && JSON.stringify(node.inputs?.reference) === JSON.stringify(reference))\n continue;\n node.params = { ...node.params, prompt: brief.prompt };\n node.inputs = { ...node.inputs, reference };\n changed++;\n }\n return changed;\n}\n\n/**\n * The product and the people (or animal) the variants were made from, by the spec's reference\n * id: every size is handed them, so the label and the faces stay the ones in the pictures. The\n * final comparison's re-laid sizes redrew a bottle's label and changed a man's face (BENCH.md § \"Final\").\n */\nfunction ownPictures(nodes: Node[], spec: AdSpec): { spec: AdSpec; refs: Record<string, string> } {\n const variant = nodes.find((n) => n.tags?.role === STATIC_VARIANT_CALL_ROLE || n.tags?.role === STATIC_VARIANTS_ROLE);\n if (!variant) return { spec, refs: {} };\n const bound = bindBriefImages(nodes as StaticCanvasNode[], variant.inputs?.reference, spec);\n const kept = new Set(bound.spec.references.filter((r) => r.role !== \"prop\").map((r) => r.id));\n const refs = Object.fromEntries(Object.entries(bound.images.refs ?? {}).filter(([id]) => kept.has(id)));\n return { spec: bound.spec, refs };\n}\n\n/** One size re-laid out from the chosen picture for its own frame. */\nfunction relayBrief(\n spec: AdSpec,\n profile: NonNullable<ReturnType<typeof imageProfileFor>>,\n opts: {\n model: string;\n format: string;\n placements: string[] | undefined;\n logo: string | undefined;\n refs: Record<string, string>;\n },\n) {\n return adaptBrief(spec, profile, {\n plan: adaptPlan(opts.model, opts.format, opts.placements),\n images: { base: MASTER_REF, ...(opts.logo ? { logo: opts.logo } : {}), refs: opts.refs },\n });\n}\n\n/** The lead's own picture of `format`, on an ad of a series that has it (`lead_ad_9x16`). */\nfunction leadSizeRef(nodes: Node[], format: string): string | undefined {\n const id = `lead_ad_${formatSlug(format)}`;\n return nodes.some((n) => n.id === id && n.tags?.role === SERIES_LEAD_ROLE) ? `$ref:${id}.asset` : undefined;\n}\n\n/**\n * One size of an ad of a series: the lead's own picture of that size with the same words replaced\n * as on this ad's picture. The lead's size was already laid out for its frame, so nothing is\n * re-laid: each ad of the series keeps the lead's layout in every size.\n */\nfunction seriesSizeBrief(\n spec: AdSpec,\n profile: NonNullable<ReturnType<typeof imageProfileFor>>,\n opts: { series: SeriesOf; format: string; base: string; logo: string | undefined; placements: string[] | undefined },\n) {\n return compileStaticBrief(spec, undefined, opts.format, undefined, profile, {\n job: \"series\",\n instruction: seriesWordsInstruction(opts.series.copy, spec.copy, wordsLanguage(opts.series)),\n images: { base: opts.base, ...(opts.logo ? { logo: opts.logo } : {}) },\n ...(opts.placements ? { placements: opts.placements } : {}),\n });\n}\n\n/** What every re-laid-out size is made from: the chosen picture at the master's pixels. */\nconst MASTER_REF = \"$ref:master.image\";\n\n/** The brand's logo file as the re-lays are handed it: none when the canvas composites it on every size instead. */\nfunction drawnLogo(nodes: Node[]): string | undefined {\n if (nodes.some((n) => n.tags?.role === LOGO_COMPOSITE_ROLE)) return undefined;\n const logo = nodes.find((n) => n.type === \"ingest\" && n.tags?.role === BRAND_LOGO_ROLE);\n return typeof logo?.id === \"string\" ? `$ref:${logo.id}.asset` : undefined;\n}\n\n/** The ad set's placements the canvas's sizes were planned with (kept on the prefilter). */\nexport function plannedPlacements(nodes: Node[]): string[] | undefined {\n const placements = nodes.find((n) => n.type === \"static_prefilter\")?.params?.placements;\n return Array.isArray(placements) && placements.every((p) => typeof p === \"string\") ? placements : undefined;\n}\n","/**\n * Which model does which image job, by the kind of concept: one table, read by every\n * static scaffold and the bench, so no call site pins a model id.\n *\n * A route changes here only for a model that has a profile (`prompt-profiles.ts`) and won its\n * class on the bench. E5 decides the reframe model.\n */\nimport {\n AD_DESCRIBE_MODEL,\n AD_IMAGE_MODEL,\n ASPECT_ADAPT_MODEL,\n PLATE_JUDGE_MODEL,\n REFERENCE_SHEET_MODEL,\n} from \"@baker/canvas-contract\";\n\n/** What kind of picture a concept is, as far as choosing its model goes. */\nexport const CONCEPT_CLASSES = [\"designed\", \"illustration\", \"photo\", \"photo_people\"] as const;\nexport type ConceptClass = (typeof CONCEPT_CLASSES)[number];\n\n/** The image jobs a static ad runs. */\nexport const IMAGE_JOBS = [\"generate\", \"fix\", \"reframe\", \"relayout\", \"identity_sheet\", \"judge\", \"describe\"] as const;\nexport type ImageJob = (typeof IMAGE_JOBS)[number];\n\nconst everyClass = (model: string): Record<ConceptClass, string> => ({\n designed: model,\n illustration: model,\n photo: model,\n photo_people: model,\n});\n\nconst ROUTES: Readonly<Record<ImageJob, Readonly<Record<ConceptClass, string>>>> = {\n // gpt-image-2.5 Sunburst in every class, Nano Banana Pro in none: on the same brief (182 paired\n // ads) Sunburst shipped 8.2% vs 6.0%, read its copy exactly in 72% vs 60% and drew an artifact\n // in 1% vs 15%; on photoreal people (9 ads) 11% vs 0% shippable, artifacts 0% vs 13%. On the\n // E7 gallery (30 rated pairs) the user picked Sunburst 68% of the time and the judge 72%.\n // Routes are chosen by results per job, never by price (the user: \"use the best one for what\n // it is\") (BENCH.md#E7).\n generate: everyClass(AD_IMAGE_MODEL),\n fix: everyClass(AD_IMAGE_MODEL),\n reframe: everyClass(ASPECT_ADAPT_MODEL),\n // Each size re-laid out from the chosen ad: gpt-image-2.5 Sunburst at max. In the E5 pilot\n // Nano Banana Pro set the ad as a card on a background and drew the layout guide's stripes\n // into it (BENCH.md § \"E5 — true per-format adaptation\").\n relayout: everyClass(AD_IMAGE_MODEL),\n identity_sheet: everyClass(REFERENCE_SHEET_MODEL),\n // Gemini Flash. Against the user's blind picks (E9, 20 pairs, the same rubric) Gemini Pro\n // agreed 55% vs Flash's 60%, so Pro bought nothing; the gain has to come from elsewhere\n // (BENCH.md § \"E9 — judge calibrated to the user\").\n judge: everyClass(PLATE_JUDGE_MODEL),\n describe: everyClass(AD_DESCRIBE_MODEL),\n};\n\n/** The model for one job on one kind of concept. Pure. */\nexport function pickImageModel(conceptClass: ConceptClass, job: ImageJob): string {\n return ROUTES[job][conceptClass];\n}\n","// Pure transform: an `image_describe` blueprint + an AI-selected list of the\n// image's MAIN identity elements → a runnable `baker-canvas/1` static-ad canvas.\n// No IO — the command layer runs the describe + element-selection passes, bakes\n// the blueprint to `prompt.json`, calls this, validates the result, and writes it.\n//\n// The image model designs the whole ad. Three days of production (Sept 2026) on the\n// other design, a text-free \"plate\" with the copy drawn over it by HTML that read the\n// picture's pixels for room, kept the ready rate flat at 40-43% whatever was patched:\n// the model paints an ad whether or not it is told to (cards, pills, wordmarks, whole\n// UI mocks with their words), and a second designer that cannot see the first collides\n// with it. So there is one designer. The model renders the finished ad from the\n// blueprint, with the copy quoted verbatim, the brand's typeface shown to it as a\n// specimen and the real logo file as a reference; three requests return three variants and a\n// judge keeps one; every size is made from that one picture (the lead size as it is, each\n// other size the chosen ad re-laid out for its frame by one request with the same words,\n// people, product and logo) (`static-sizes.ts`, `static-topology.ts`); the run then reads\n// every word back (static-review.ts) and refuses to call the ad ready when one is off.\n// No render reads the blueprint: each carries the brief `static-brief.ts` compiles from the\n// ad spec in prompt.json (`static-render-briefs.ts`), re-projected on every run.\n// Shares its shape with `video.ts` (tolerant schema, `[TODO]` markers, a `*Report`).\n\nimport { type AdFormatPlatform, deliverySet, imageGenerateAspectRatios, platformFormats } from \"@baker/canvas-contract\";\nimport { z } from \"zod\";\nimport { pickImageModel } from \"../lib/image-model-router.ts\";\nimport { GPT_IMAGE_25_PROFILE, imageProfileFor } from \"../lib/prompt-profiles.ts\";\nimport type { Canvas, NodeDecl } from \"../schema/canvas.ts\";\nimport { avatarsByPersona, avatarsLeftUncast, type CastAvatar, castAvatarsInOrder } from \"./castAvatar.ts\";\nimport { assignElementLabels, sanitizeId } from \"./lib/elements.ts\";\nimport { imageRenderParams } from \"./lib/image-params.ts\";\nimport {\n AD_SPEC_SCHEMA,\n type AdBrand,\n type AdReference,\n type AdSpec,\n AdSpecSchema,\n adSpecFromBlueprint,\n conceptClassOf,\n plainText,\n withRenderedLettering,\n} from \"./static-ad-spec.ts\";\nimport { formatOfAdaptId, projectAdaptBriefs } from \"./static-adapt-briefs.ts\";\nimport { copyStrings, inSceneText, specimenText, type WinningMechanism, winningMechanisms } from \"./static-copy.ts\";\nimport type { FontOrigin, ResolvedFont } from \"./static-fonts.ts\";\nimport { BRAND_LOGO_ROLE, fixBriefNode, projectStaticBriefs, STATIC_AD_ROLE } from \"./static-render-briefs.ts\";\nimport {\n DEFAULT_MASTER_SHAPE,\n type MasterShape,\n planStaticFormats,\n type SizePlan,\n type StaticFormatPlan,\n} from \"./static-sizes.ts\";\nimport {\n formatSlug,\n LOGO_COMPOSITE_DEFAULT,\n nodesWithRole,\n RENDER_GRAPH_IDS,\n SERIES_LEAD_ROLE,\n STATIC_ADAPT_FORMAT_ROLE,\n STATIC_FIX_ROLE,\n STATIC_MASTER_ROLE,\n STATIC_PICK_ROLE,\n STATIC_VARIANT_CALL_ROLE,\n STATIC_VARIANTS_ROLE,\n staticMasterFormat,\n staticRenderNodes,\n VARIANT_PREFILTER_ROLE,\n} from \"./static-topology.ts\";\n\n// Ratios at least one curated image model renders, read off the registry; the\n// blueprint's estimate is honored only when it lands in this set, else we default to\n// a vertical social format. The render model then snaps it to its own nearest shape.\nconst GEN_ASPECT_RATIOS = new Set(imageGenerateAspectRatios());\nconst DEFAULT_ASPECT_RATIO = \"9:16\";\n\n// The tier every render asks for; snapped down per model (Nano Banana 2 Lite is 1K).\nconst AD_IMAGE_SIZE = \"2K\";\n\n// --- Tolerant blueprint schema: only the fields the scaffold itself consumes. ---\nconst Blueprint = z\n .object({\n meta: z.object({ estimated_aspect_ratio: z.string().optional() }).loose().optional(),\n text_content: z.array(z.object({ text: z.string().optional() }).loose()).optional(),\n people: z.array(z.unknown()).optional(),\n fonts: z.array(z.unknown()).optional(),\n winning_mechanisms: z.array(z.unknown()).optional(),\n })\n .loose();\ntype StaticAdBlueprint = z.infer<typeof Blueprint>;\n\n/**\n * The parts of the describe contract a static ad is rebuilt from that the blueprint lacks.\n * Named rather than silently tolerated: without `winning_mechanisms` the rewrite keeps the\n * surface and loses what made the ad work, and without `fonts` the letterforms go with it.\n */\nfunction describeGaps(blueprint: StaticAdBlueprint): Array<\"fonts\" | \"winning_mechanisms\"> {\n const gaps: Array<\"fonts\" | \"winning_mechanisms\"> = [];\n if (!blueprint.fonts?.length) gaps.push(\"fonts\");\n if (winningMechanisms(blueprint).length === 0) gaps.push(\"winning_mechanisms\");\n return gaps;\n}\n\n// --- A main element chosen by the AI selection pass. ---\nconst MainElement = z\n .object({\n // logo | product | person | animal | badge | other\n type: z.string(),\n label: z.string().optional(),\n description: z.string().optional(),\n expression: z.string().nullable().optional(),\n reason: z.string().optional(),\n })\n .loose();\ntype MainElementT = z.infer<typeof MainElement>;\nconst MainElements = z.array(MainElement);\n\n// A static ad's selection never links two looks of one person, and it never sheets a\n// person, so a cast slot needs no telling that its picture is already a turnaround.\nconst STATIC_CASTING = { looks: false, markSheet: false };\n\n/**\n * Cast company Avatars into the person slots: the avatar that best matches `persona` (who the ad\n * is for) first, else in the order given; the first person gets the first, and so on. The slot takes the avatar's identity sheet as its picture and its\n * subject description as the words, so the render shows the brand's own cast. A person\n * left without an avatar keeps its TODO: casting nobody is how a canvas renders a stranger.\n */\nexport function castAvatarsOnStaticElements(\n elementsInput: unknown,\n avatars: readonly CastAvatar[],\n persona?: string,\n): MainElementT[] {\n return castAvatarsInOrder(MainElements.parse(elementsInput), avatarsByPersona(avatars, persona), STATIC_CASTING);\n}\n\n/**\n * The handles `castAvatarsOnStaticElements` had no person for, in the order given: read off the\n * cast elements, since a persona may cast them in another order than they were passed.\n */\nexport function staticAvatarsNotCast(castElements: unknown, handles: readonly string[]): string[] {\n const elements = MainElements.parse(castElements);\n if (!elements.some((el) => typeof el.cast_handle === \"string\")) {\n return avatarsLeftUncast(elements, handles, STATIC_CASTING);\n }\n return handles.filter((h) => !elements.some((el) => el.cast_handle === h));\n}\n\n/** The picture an element arrived with (a cast Avatar's sheet URL), if any. */\nfunction suppliedImage(el: MainElementT): string | undefined {\n const url = typeof el.source_image === \"string\" ? el.source_image.trim() : \"\";\n return /^https?:\\/\\//i.test(url) ? url : undefined;\n}\n\n/** Where a static ad's brand lives in the workspace, as paths relative to the canvas file. */\ntype StaticAdBrand = {\n /** The brand's logo file, shown to the model as a reference. Only when the concept shows a logo; never generated. */\n logoPath?: string;\n /** The brand's headline face: a font file, or the family the brand names as a web font. Shown to the model as a type specimen. */\n font?: ResolvedFont;\n};\n\nexport type StaticAdOpts = {\n /** image_generate model id (resolved from the registry by the command). */\n genModel: string;\n /** Source image the emitted `original` ingest reads (the record of the source; a first render sees it for its structure only when prompt.json keeps its concept) — a local path or, when `imageIsUrl`, a URL. */\n imagePath?: string;\n /** When true, `imagePath` is a URL and the `original` ingest uses `source: \"url\"`. */\n imageIsUrl?: boolean;\n /** Path the emitted `prompt` ingest reads (the baked blueprint). Default ./prompt.json */\n blueprintPath?: string;\n /** Override the aspect ratio; otherwise taken from the blueprint, else 9:16. */\n aspectRatio?: string;\n /** The placement set the ad is delivered in; undefined = the single base format. */\n placements?: AdFormatPlatform;\n /** The brand's logo and typeface. */\n brand?: StaticAdBrand;\n /** Variants rendered and judged. Default 3; 1 skips the judge. */\n takes?: number;\n /** How the one master picture is shaped and the sizes made from it. Default `DEFAULT_MASTER_SHAPE`. */\n masterShape?: MasterShape;\n /** The ad set's Meta placements (the viewer's), which narrow the safe areas; none = Advantage+, Reels included. */\n adPlacements?: readonly string[];\n /**\n * The platforms (or placement ids) the ad runs on (`@baker/canvas-contract` `AD_PLATFORMS`):\n * the delivered sizes are every required size of their placements, and each size keeps clear\n * of its placements' interface. Wins over `placements`.\n */\n platforms?: readonly string[];\n /** Composite the brand's logo file on every size instead of having the model draw it. Default off (bench E6). */\n logoComposite?: boolean;\n /**\n * The ad spec the renders compile from (what prompt.json holds). Default: the one\n * `staticAdSpec` derives from the blueprint and the elements.\n */\n spec?: unknown;\n};\n\n/**\n * A typographic reference: it carries copy and pictures nothing (no subject, no person).\n * Reported so the agent knows the ad is its words on the brand's colours.\n */\nexport function isTextForward(input: unknown): boolean {\n const blueprint = input as { subjects?: unknown; people?: unknown } | null;\n const none = (v: unknown) => !Array.isArray(v) || v.length === 0;\n return none(blueprint?.subjects) && none(blueprint?.people) && copyStrings(input).length > 0;\n}\n\n/**\n * The logo is on the ad when the ad spec keeps it AND the brand has the real file. The reference\n * ad never decides: audited Sept 2026, whether our ad carried our logo followed whether the\n * reference showed its own.\n */\nfunction logoDrawn(spec: AdSpec, opts: StaticAdOpts): boolean {\n return Boolean(opts.brand?.logoPath) && spec.logo;\n}\n\n/** The ad spec a canvas compiles from: the one given, else the one the blueprint reads as, keeping the logo when the brand has one. */\nfunction specFor(input: unknown, elements: MainElementT[], opts: StaticAdOpts): AdSpec {\n return opts.spec === undefined\n ? staticAdSpec(input, elements, { logo: Boolean(opts.brand?.logoPath) })\n : AdSpecSchema.parse(opts.spec);\n}\n\n/** Whether the logo file is composited on every size (else the model draws it from the file). */\nfunction logoComposited(opts: StaticAdOpts): boolean {\n return opts.logoComposite ?? LOGO_COMPOSITE_DEFAULT;\n}\n\n/** Candidates rendered at most: the three directions a brief can take (`static-brief.ts`). */\nconst MAX_TAKES = 3;\n\n/** A render's prompt before its brief is projected into it (never sent: the scaffold projects before it returns). */\nconst UNCOMPILED = \"compiled from prompt.json\";\n\nfunction ingestNode(id: string, path: string, expect: \"image\" | \"font\" | \"json\"): NodeDecl {\n return { id, type: \"ingest\", params: { source: \"path\", path, expect } };\n}\n\nfunction aspectRatio(blueprint: StaticAdBlueprint, opts: StaticAdOpts): string {\n const requested = opts.aspectRatio ?? blueprint.meta?.estimated_aspect_ratio;\n return requested && GEN_ASPECT_RATIOS.has(requested) ? requested : DEFAULT_ASPECT_RATIO;\n}\n\n/**\n * Every format this canvas delivers: the placement's set, or just the requested ratio.\n * The requested ratio (the source's, or `--aspect`) leads when the set has it, because\n * the ad is rendered for the first format and the source layout was drawn for it.\n */\nfunction outputFormats(blueprint: StaticAdBlueprint, opts: StaticAdOpts): string[] {\n const requested = aspectRatio(blueprint, opts);\n const delivery = opts.platforms?.length ? deliverySet(opts.platforms) : null;\n if (delivery && delivery.formats.length === 0 && delivery.banners.length > 0) {\n throw new Error(\n `--platforms ${opts.platforms?.join(\",\")} requires only fixed-size banners (${delivery.banners.map((b) => b.format).join(\", \")}), which are never made from the picture: add a platform with a feed or story size (meta, google_display, …).`,\n );\n }\n const destination = delivery && delivery.formats.length > 0 ? delivery.formats.map((f) => f.format) : null;\n if (!destination && !opts.placements) return [requested];\n const formats = destination ?? [...platformFormats(opts.placements as AdFormatPlatform)];\n return formats.includes(requested) ? [requested, ...formats.filter((f) => f !== requested)] : formats;\n}\n\n/**\n * The placements the safe areas keep clear of: the ad set's own when it was read (Meta's only, so\n * every other platform named keeps its own), else every placement of the platforms named, else\n * none (Advantage+, Reels included).\n */\nfunction safePlacements(opts: StaticAdOpts): readonly string[] | undefined {\n const named = opts.platforms?.length ? deliverySet(opts.platforms).formats.flatMap((f) => f.placements) : [];\n if (opts.adPlacements) return [...new Set([...opts.adPlacements, ...named.filter((p) => !p.startsWith(\"meta_\"))])];\n return named.length > 0 ? [...new Set(named)] : undefined;\n}\n\ntype Slot = { ref: string; label: string; type: string };\n\ntype Built = { nodes: NodeDecl[] };\n\n/** Ids the spine and the brand nodes take, so no element slot collides with them. */\nconst RESERVED_IDS = [\"prompt\", \"original\", \"brand_logo\", \"font_headline\", \"type_specimen\", ...RENDER_GRAPH_IDS];\n\n/** Each element with its role-based label and the id of the ingest that holds its picture. */\nfunction elementSlots(elements: MainElementT[]): Array<{ el: MainElementT; label: string; id: string }> {\n const used = new Set(RESERVED_IDS);\n return assignElementLabels(elements, \"static\").map(({ el, label }, i) => {\n let id = sanitizeId(`el_${label}`, `el_${i}`);\n while (used.has(id)) id = `${id}_${i}`;\n used.add(id);\n return { el, label, id };\n });\n}\n\n/**\n * The pictures the ad grounds on: each person cast from the company's avatars (its identity\n * sheet), and the brand's logo. The reference ad's own people, products, animals and badges are\n * never slots to fill: they are the source's world (audited Sept 2026: they became required\n * [TODO] slots and 8 chats hand-edited canvas.json 64 times to get rid of them). A picture of our\n * own goes in prompt.json, on the reference that shows it (`image`), and every run wires it.\n * The brand's real logo file is wired whenever the ad spec keeps the logo (never a stand-in).\n */\nfunction elementNodes(\n elements: MainElementT[],\n opts: StaticAdOpts,\n drawLogo: boolean,\n): Built & { slots: Slot[]; logoRef: string | undefined } {\n const nodes: NodeDecl[] = [];\n const slots: Slot[] = [];\n elementSlots(elements).forEach(({ el, label, id }) => {\n const url = suppliedImage(el);\n if (el.type.toLowerCase() === \"logo\" || !url) return;\n nodes.push({ id, type: \"ingest\", params: { source: \"url\", url, expect: \"image\" } });\n slots.push({ ref: `$ref:${id}.asset`, label, type: el.type });\n });\n const logoPath = drawLogo ? opts.brand?.logoPath : undefined;\n if (!logoPath) return { nodes, slots, logoRef: undefined };\n nodes.push({ ...ingestNode(\"brand_logo\", logoPath, \"image\"), tags: { role: BRAND_LOGO_ROLE } });\n return { nodes, slots, logoRef: \"$ref:brand_logo.asset\" };\n}\n\n/**\n * The brand's typeface, shown to the model as a specimen set on the ad's own copy: the\n * font file (or the web font file by URL) staged like any asset, then `font_specimen`\n * renders the letters the ad needs. `syncStaticAdBrief` keeps the specimen's text in\n * step with prompt.json.\n */\nfunction typeNodes(font: ResolvedFont | undefined, copy: string[]): Built & { ref: string | undefined } {\n if (!font) return { nodes: [], ref: undefined };\n const ingest: NodeDecl =\n \"path\" in font.slot\n ? ingestNode(\"font_headline\", font.slot.path, \"font\")\n : { id: \"font_headline\", type: \"ingest\", params: { source: \"url\", url: font.slot.url, expect: \"font\" } };\n return {\n nodes: [\n ingest,\n {\n id: \"type_specimen\",\n type: \"font_specimen\",\n inputs: { font: \"$ref:font_headline.asset\" },\n params: { text: specimenText(copy) },\n },\n ],\n ref: \"$ref:type_specimen.image\",\n };\n}\n\n/**\n * What every render asks the model for, besides the prompt and the shape: the size and the\n * quality step its profile asks for (`prompt-profiles.ts`), snapped to what the model has.\n */\nexport function renderParamsFor(genModel: string): Record<string, unknown> {\n const want = (imageProfileFor(genModel) ?? GPT_IMAGE_25_PROFILE).brief.params;\n return imageRenderParams(genModel, { size: want.imageSize ?? AD_IMAGE_SIZE, quality: want.quality ?? \"xhigh\" });\n}\n\n/** Candidates rendered: 1-3 (default 3). */\nfunction takesFor(opts: StaticAdOpts): number {\n return Math.max(1, Math.min(MAX_TAKES, opts.takes ?? MAX_TAKES));\n}\n\n/**\n * The render graph from the element slots: the variants (their prompts are the compiled\n * brief, projected by `scaffoldStaticAd` before it returns), the judge, the master and every\n * size. The source ad is not among the pictures: a competitor's image is the strongest\n * conditioning a render gets, and it came back as a near-copy (prompt-audit.md §4.3; bench E2\n * decides whether it ever returns, as a layout-only reference). The logo file goes to the\n * model, or is composited on every size, never both.\n */\nfunction renderGraph(args: {\n slots: Slot[];\n logoRef: string | undefined;\n typeRef: string | undefined;\n opts: StaticAdOpts;\n plan: StaticFormatPlan;\n judgeModel: string;\n adaptModel: string;\n relayoutModel: string;\n}): NodeDecl[] {\n const { opts } = args;\n const composite = args.logoRef !== undefined && logoComposited(opts);\n return staticRenderNodes({\n genModel: opts.genModel,\n genParams: renderParamsFor(opts.genModel),\n takes: takesFor(opts),\n reference: [\n ...args.slots.map((s) => s.ref),\n ...(args.logoRef && !composite ? [args.logoRef] : []),\n ...(args.typeRef ? [args.typeRef] : []),\n ],\n specRef: \"$ref:prompt.asset\",\n judgeModel: args.judgeModel,\n adaptModel: args.adaptModel,\n relayoutModel: args.relayoutModel,\n ...(args.logoRef && !composite ? { logoRef: args.logoRef } : {}),\n plan: args.plan,\n ...(composite && args.logoRef ? { compositeLogo: args.logoRef } : {}),\n prompt: UNCOMPILED,\n });\n}\n\n/** Who the face is, for a person reading the report: the family, and where it came from. */\nfunction fontLabel(font: ResolvedFont): string {\n const origin: Record<FontOrigin, string> = {\n brand_file: \"the brand's font file\",\n tokens: \"named in tokens.json, loaded as a web font\",\n brand_md: \"named in BRAND.md, loaded as a web font\",\n };\n const name =\n font.family ?? (\"path\" in font.slot ? (font.slot.path.split(\"/\").pop() ?? font.slot.path) : \"a web font\");\n return `${name} (${origin[font.origin]})`;\n}\n\n/** How the variants come back, in one clause. */\nfunction describeVariants(takes: number): string {\n const numbers = [\"No\", \"One\", \"Two\", \"Three\"];\n if (takes <= 1) return \"One finished ad is rendered (no judge)\";\n const n = numbers[takes] ?? String(takes);\n return `${n} variants of the finished ad are rendered in parallel, one request each, and a judge keeps one`;\n}\n\n/**\n * How every size is made from the one chosen picture, in one sentence: the scaffold's\n * description and report and an upgraded canvas's description all say it the same way.\n */\nexport function describeSizes(plan: StaticFormatPlan): string {\n const of = (from: SizePlan[\"from\"]) => plan.sizes.filter((s) => s.from === from).map((s) => s.format);\n const cuts = of(\"crop\");\n const adapted = of(\"adapt\");\n const relaid = of(\"relayout\");\n const cut =\n cuts.length > 0\n ? `; ${cuts.join(\" and \")} ${cuts.length === 1 ? \"is a cut\" : \"are cuts\"} of it that ${cuts.length === 1 ? \"keeps\" : \"keep\"} every word inside that size's safe area`\n : \"\";\n const adapt =\n adapted.length > 0\n ? `; ${adapted.join(\" and \")} ${adapted.length === 1 ? \"is\" : \"are\"} adapted from it by the reframe model (one billed request)`\n : \"\";\n const relayout =\n relaid.length > 0\n ? `; ${relaid.join(\" and \")} ${relaid.length === 1 ? \"is\" : \"are\"} the chosen ad re-laid out for that size's frame by the image model (one billed request per size): the same words, people, product and logo, re-scaled for the frame and never cut`\n : \"\";\n const closing = relaid.length > 0 ? \"\" : \" No size is rendered again.\";\n return `Every size is made from the chosen picture, rendered once at ${plan.master.format}${relayout}${cut}${adapt}.${closing}`;\n}\n\n/** What a run of this canvas actually does, in one paragraph: the report and the description share it. */\nfunction describeRun(args: {\n takes: number;\n logo: \"model\" | \"composite\" | null;\n font?: ResolvedFont;\n copy: number;\n inScene: number;\n plan: StaticFormatPlan;\n}): string {\n const text =\n args.copy === 0\n ? \" The ad has no copy: the picture is the ad.\"\n : args.font\n ? ` The copy is set by the model in ${fontLabel(args.font)}, shown to it as a specimen, and read back word for word by the review.`\n : \" The copy is set by the model (no brand typeface could be loaded) and read back word for word by the review.\";\n const scene = args.inScene > 0 ? ` ${args.inScene} line(s) are written on something in the scene.` : \"\";\n const mark =\n args.logo === \"composite\"\n ? \" The brand's logo file is placed on every size, inside its safe area; the model draws no logo.\"\n : args.logo === \"model\"\n ? \" The brand's logo file is given to the model to reproduce where the concept places it.\"\n : \" No logo is drawn: the concept has none, or the brand has no logo file (Meta shows the page name and picture on every ad).\";\n return `${describeVariants(args.takes)}. ${describeSizes(args.plan)}${text}${scene}${mark}`;\n}\n\n/**\n * Assemble a static-ad canvas: variants of the finished ad, one request each, a deterministic\n * prefilter and a judge, the\n * chosen picture at the master shape, and every delivered size made from it without another\n * generation (`static-topology.ts`).\n */\nexport function scaffoldStaticAd(input: unknown, elementsInput: unknown, opts: StaticAdOpts): Canvas {\n const blueprint = Blueprint.parse(input);\n const elements = MainElements.parse(elementsInput);\n const plan = planStaticFormats(opts.genModel, outputFormats(blueprint, opts), {\n masterShape: opts.masterShape ?? DEFAULT_MASTER_SHAPE,\n ...(safePlacements(opts) ? { placements: safePlacements(opts) } : {}),\n });\n const copy = copyStrings(blueprint);\n const spec = specFor(input, elements, opts);\n const drawLogo = logoDrawn(spec, opts);\n // As rendered: handwritten in-scene copy is set as designed type (`withRenderedLettering`).\n const designed = withRenderedLettering(spec)\n .copy.filter((l) => l.lettering === \"designed\")\n .map((l) => l.text);\n\n const spine: NodeDecl[] = [\n { ...ingestNode(\"prompt\", opts.blueprintPath ?? \"./prompt.json\", \"json\"), tags: { role: STATIC_AD_ROLE } },\n // An ad started from an idea (`baker creatives make` with no reference) has no source picture.\n ...(opts.imagePath\n ? [\n {\n id: \"original\",\n type: \"ingest\",\n params: opts.imageIsUrl\n ? { source: \"url\", url: opts.imagePath, expect: \"image\" }\n : { source: \"path\", path: opts.imagePath, expect: \"image\" },\n },\n ]\n : []),\n ];\n const els = elementNodes(elements, opts, drawLogo);\n // The type specimen serves the lines set as designed type; in-scene lettering keeps its own.\n const type = typeNodes(designed.length > 0 ? opts.brand?.font : undefined, designed);\n const conceptClass = conceptClassOf(spec);\n const render = renderGraph({\n slots: els.slots,\n logoRef: els.logoRef,\n typeRef: type.ref,\n opts,\n plan,\n judgeModel: pickImageModel(conceptClass, \"judge\"),\n adaptModel: pickImageModel(conceptClass, \"reframe\"),\n relayoutModel: pickImageModel(conceptClass, \"relayout\"),\n });\n\n const canvas: Canvas = {\n schema: \"baker-canvas/1\",\n metadata: {\n name: \"static ad reproduction\",\n description: `Scaffolded by \\`baker canvas scaffold-static-ad\\`. Edit \\`prompt.json\\` (the ad spec read from the source image) into the ad you want; every render prompt is compiled from it on every run, and every picture it names (\\`references[].image\\`, the brand's logo) is wired from it. Then \\`baker canvas run\\`. ${describeRun(\n {\n takes: takesFor(opts),\n logo: drawLogo ? (logoComposited(opts) ? \"composite\" : \"model\") : null,\n font: opts.brand?.font,\n copy: copy.length,\n inScene: inSceneText(blueprint).length,\n plan,\n },\n )}`,\n },\n nodes: [...spine, ...els.nodes, ...type.nodes, ...render],\n output: { node: \"final\", output: \"images\" },\n };\n projectStaticBriefs(canvas, spec);\n projectAdaptBriefs(canvas, spec);\n return canvas;\n}\n\n/** A blueprint entry, read loosely: the describe pass's shape is not a contract here. */\ntype Loose = Record<string, unknown>;\n\n/** The names of the source ad's advertiser and every brand it shows: none may reach a render of ours. */\nfunction sourceBrandNames(bp: Loose): string[] {\n const context = bp.source_context as Loose | undefined;\n const reference = bp.reference as Loose | undefined;\n const logos = Array.isArray(bp.brands_logos) ? (bp.brands_logos as Loose[]) : [];\n return [context?.advertiser_brand, reference?.advertiser, ...logos.map((l) => l?.brand_name)].filter(\n (n): n is string => typeof n === \"string\" && n.trim().length > 1,\n );\n}\n\n/**\n * The concept a new spec starts from: the fit made of the reference when there is one (it knows\n * what we have), else the describe pass's read of why it wins, every load-bearing element marked\n * missing until the agent names what of ours carries it (a run refuses it until then: a render\n * that kept \"the free AirPods\" of a Rippling ad would show AirPods we do not give). Nothing when\n * the read names no load-bearing element.\n */\nfunction conceptSeed(\n bp: Loose,\n fit: Pick<AdSpec, \"concept\" | \"brand_presence\" | \"recognition\"> | undefined,\n scrub: readonly string[],\n) {\n if (fit?.concept) {\n return {\n concept: fit.concept,\n ...(fit.brand_presence ? { brand_presence: fit.brand_presence } : {}),\n ...(fit.recognition ? { recognition: fit.recognition } : {}),\n };\n }\n const loadBearing = (Array.isArray(bp.load_bearing) ? (bp.load_bearing as Loose[]) : []).flatMap((l) => {\n const element = plainText(l?.element, scrub);\n const needed = plainText(l?.needs, scrub);\n const needs = needed ? { needs: needed } : {};\n return element ? [{ element, ...needs, have: \"missing\" as const }] : [];\n });\n if (loadBearing.length === 0) return {};\n const why = (bp.why_it_wins && typeof bp.why_it_wins === \"object\" ? bp.why_it_wins : {}) as Loose;\n const layers = Object.fromEntries(\n ([\"disguise\", \"voice\", \"trigger\", \"attention\", \"logic\"] as const).flatMap((k) =>\n plainText(why[k], scrub) ? [[k, plainText(why[k], scrub)]] : [],\n ),\n );\n return { concept: { why_it_wins: layers, load_bearing: loadBearing, verdict: \"adapt\" as const } };\n}\n\nconst REFERENCE_KIND: Record<string, AdReference[\"role\"]> = {\n person: \"identity\",\n animal: \"identity\",\n product: \"product\",\n};\n\n/**\n * The ad spec a new static ad starts from, written to prompt.json for the agent to rewrite:\n * the source ad's copy (each line marked lettered-in-scene or designed), its scene, its\n * mechanism, and one reference per element slot, labelled by role, with the slot that holds\n * its picture. The source's advertiser and every brand it shows are scrubbed from the text;\n * the source's own read stays as bookkeeping (`source`, `reference`), never compiled.\n */\nexport function staticAdSpec(\n blueprintInput: unknown,\n elementsInput: unknown,\n extras: {\n brand?: AdBrand | null;\n formats?: readonly string[];\n logo?: boolean;\n /** The fit made of the reference (`baker winning-ads fit`, `conceptFromFit`): seeds the concept. */\n fit?: Pick<AdSpec, \"concept\" | \"brand_presence\" | \"recognition\">;\n /** Where the ad runs (the ad set's countries, else COMPANY.md): written into the viewer. */\n market?: NonNullable<AdSpec[\"viewer\"]>[\"market\"];\n } = {},\n): AdSpec {\n const bp = (blueprintInput && typeof blueprintInput === \"object\" ? blueprintInput : {}) as Loose;\n const scrub = sourceBrandNames(bp);\n const base = adSpecFromBlueprint(bp, { scrub });\n // Only our own cast is a reference: the source's products are a competitor's, its people strangers.\n const references: AdReference[] = elementSlots(MainElements.parse(elementsInput)).flatMap(({ el, label, id }) => {\n const type = el.type.toLowerCase();\n if (type === \"logo\" || !suppliedImage(el)) return [];\n const described = [el.description, el.expression ? `with a ${el.expression} expression` : \"\"]\n .filter(Boolean)\n .join(\", \");\n const what = plainText(described, scrub) || `the ${type} in this picture`;\n const avatar = typeof el.cast_handle === \"string\" ? { avatar: el.cast_handle } : {};\n return [{ id: label, role: REFERENCE_KIND[type] ?? \"prop\", slot: id, what, ...avatar }];\n });\n const context = bp.source_context as Loose | undefined;\n const source = {\n ...(typeof context?.advertiser_brand === \"string\" ? { advertiser: context.advertiser_brand } : {}),\n ...(bp.audience !== undefined ? { audience: bp.audience } : {}),\n ...(bp.mechanism !== undefined ? { mechanism: bp.mechanism } : {}),\n };\n return AdSpecSchema.parse({\n ...base,\n schema: AD_SPEC_SCHEMA,\n references,\n ...(extras.brand ? { brand: extras.brand } : {}),\n ...(extras.formats ? { formats: [...extras.formats] } : {}),\n ...(extras.logo !== undefined ? { logo: extras.logo } : {}),\n ...conceptSeed(bp, extras.fit, scrub),\n ...(extras.market\n ? { viewer: { ...(base.viewer ?? { who: \"the people this ad is for\" }), market: extras.market } }\n : {}),\n ...(Object.keys(source).length > 0 ? { source } : {}),\n });\n}\n\n/**\n * The canvas re-rendered as a correction of a finished version: one render that takes the\n * previous ad as its first reference and changes only what `instruction` says, in place of\n * the variants, the prefilter and the judge. It feeds the master, so every size is made from\n * the corrected picture exactly as from a chosen one (each re-laid-out size renders again, its\n * input changed; nothing else renders). A correction is\n * a fresh render from a right prompt's own result, so a good version is kept rather than\n * re-rolled: the picture, the layout and the words the person already liked survive the fix.\n * `promptJson` is the content of prompt.json, which the correction's brief compiles from.\n *\n * Only for a version whose graph was never kept (a run recorded before graphs were): any other\n * version is corrected on top of its own graph instead, its variants and pick kept (`growFromVersion`).\n *\n * Pure: the scaffolded canvas in, the correction canvas out; nothing is written.\n */\nexport function fixStaticAd(\n input: unknown,\n opts: { instruction: string; previous: string; promptJson: unknown },\n): Canvas {\n const canvas = input as Canvas;\n const stage = variantStage(canvas);\n // The previous ad is the picture to edit; the identity references (subjects, logo, type)\n // stay so the fix keeps them exact.\n const masterFormat = staticMasterFormat(canvas);\n const brief = fixBriefNode(canvas, stage.template as never, opts.promptJson, {\n instruction: opts.instruction,\n previous: \"$ref:previous.asset\",\n ...(masterFormat ? { format: masterFormat } : {}),\n drop: stage.removed,\n }) as NodeDecl;\n const params: Record<string, unknown> = { ...brief.params };\n delete params.n;\n const fix: NodeDecl = { ...brief, id: \"fix\", params, tags: { role: STATIC_FIX_ROLE } };\n const previous: NodeDecl = {\n id: \"previous\",\n type: \"ingest\",\n params: { source: \"url\", url: opts.previous, expect: \"image\" },\n };\n return withMasterSource(canvas, stage, [previous, fix], \"$ref:fix.images#0\", {\n note: `This run corrects a finished version: ${opts.instruction.trim()}`,\n });\n}\n\n/**\n * The canvas re-run on a variant the person chose over the judge's pick: the chosen picture\n * (by the URL its run recorded) takes the place of the variants, the prefilter and the judge,\n * and feeds the master, so every size is made again from it; the only requests are the\n * re-laid-out sizes, whose input changed. The ingest\n * carries the judge's role, so the dashboard badges the chosen picture as this version's pick.\n * Only for a version whose graph was never kept (`growFromVersion` otherwise).\n *\n * Pure: the scaffolded canvas in, the re-derive canvas out; nothing is written.\n */\nexport function rederiveFromVariant(input: unknown, opts: { url: string }): Canvas {\n const canvas = input as Canvas;\n const stage = variantStage(canvas);\n const chosen: NodeDecl = {\n id: \"chosen\",\n type: \"ingest\",\n params: { source: \"url\", url: opts.url, expect: \"image\" },\n tags: { role: STATIC_PICK_ROLE },\n };\n const relaid = nodesWithRole(canvas, STATIC_ADAPT_FORMAT_ROLE).length;\n return withMasterSource(canvas, stage, [chosen], \"$ref:chosen.asset\", {\n note:\n relaid > 0\n ? `This run makes every size again from the variant the person chose; each other size is re-laid out from it (${relaid} ${relaid === 1 ? \"request\" : \"requests\"}).`\n : \"This run makes every size again from the variant the person chose; nothing is rendered.\",\n });\n}\n\n/** The lead ad's version a series ad is made from: its words and its finished pictures. */\nexport type SeriesLead = {\n slug: string;\n runId: string;\n /** The lead's words as that version drew them. */\n copy: Array<{ text: string; role?: string }>;\n /** The lead's chosen picture, at its master's shape. */\n picture: string;\n /** Each size the lead delivered, by format: a size re-laid out from it is edited from the lead's own. */\n sizes: Readonly<Record<string, string>>;\n /** The lead's design the ad's prompt.json was last given (`seriesDesignOf`), so an edit made on the ad itself is told apart. */\n design?: string;\n};\n\n/** The id of the ingest holding the lead's picture of one re-laid-out size: `lead_ad_9x16`. */\nexport function seriesLeadSizeId(format: string): string {\n return `lead_ad_${formatSlug(format)}`;\n}\n\n/**\n * The canvas of the next ad of a series, from the lead's canvas: one render takes the lead's\n * finished picture (`lead_ad`) and changes only its words, in place of the variants and the judge;\n * each re-laid-out size is the lead's own delivered size with the same words changed, so no size\n * is designed again and the series cannot drift. A size the lead did not deliver is re-laid out\n * from this ad's picture as usual; the cut sizes are cut from it as the lead's are. The reference\n * ad is dropped: the lead's picture already carries everything it gave. Its words come from this\n * ad's prompt.json; the lead's (`metadata.series.copy`) say which line each one replaces.\n *\n * `language` marks a translation of the lead: the edit names the language its words are in.\n *\n * Pure: the lead's scaffolded canvas in, the series ad's canvas out; nothing is written.\n */\nexport function seriesMemberAd(input: unknown, lead: SeriesLead, opts: { language?: string } = {}): Canvas {\n const canvas = withoutLeadPictures(input as Canvas);\n const stage = variantStage(canvas);\n const wired = stage.template.inputs?.reference;\n const kept = (Array.isArray(wired) ? wired : []).filter(\n (r): r is string => typeof r === \"string\" && !r.startsWith(\"$ref:original.\") && !stage.removed.has(refNode(r)),\n );\n const params: Record<string, unknown> = { ...stage.template.params };\n delete params.n;\n const leadAd = leadPicture(\"lead_ad\", lead.picture);\n const gen: NodeDecl = {\n id: \"gen\",\n type: \"image_generate\",\n inputs: { ...stage.template.inputs, reference: [`$ref:${leadAd.id}.asset`, ...kept] },\n params,\n tags: { role: STATIC_VARIANTS_ROLE },\n };\n const edited = withMasterSource(canvas, stage, [leadAd, gen], \"$ref:gen.images#0\", { note: \"\" });\n const nodes = edited.nodes\n .filter((n) => n.id !== \"original\")\n .flatMap((n) => {\n const format = n.tags?.role === STATIC_ADAPT_FORMAT_ROLE ? formatOfAdaptId(n.id) : null;\n const url = format ? lead.sizes[format] : undefined;\n if (!format || !url) return [n];\n const source = leadPicture(seriesLeadSizeId(format), url);\n const refs = Array.isArray(n.inputs?.reference) ? (n.inputs.reference as unknown[]) : [];\n const rest = refs.filter((r) => typeof r === \"string\" && refNode(r) !== stage.masterId);\n return [source, { ...n, inputs: { ...n.inputs, reference: [`$ref:${source.id}.asset`, ...rest] } }];\n });\n return {\n ...edited,\n metadata: {\n ...canvas.metadata,\n name: \"static ad of a series\",\n description: `The next ad of the series led by \\`${lead.slug}\\`: the lead's finished picture (\\`lead_ad\\`) with only its words changed to the copy in this ad's \\`prompt.json\\`, every other size the lead's own size with the same words changed. Edit only the copy in \\`prompt.json\\`, then \\`baker canvas run\\`. Change the lead to change the design of the whole series.`,\n series: {\n lead: lead.slug,\n run_id: lead.runId,\n copy: lead.copy.map((l) => ({ ...l })),\n ...(lead.design ? { design: lead.design } : {}),\n // A translation of the lead: its words are in this language, the lead's in another.\n ...(opts.language ? { language: opts.language } : {}),\n },\n },\n nodes,\n };\n}\n\n/**\n * A lead that is itself an ad of a series (an angle ad leading its translations) has a canvas made\n * from its own lead's pictures: those are dropped, each size re-laid out from the master again, so\n * the next ad is made from this lead's pictures alone. A canvas with no lead picture is returned as is.\n */\nfunction withoutLeadPictures(canvas: Canvas): Canvas {\n const leads = new Set(canvas.nodes.filter((n) => n.tags?.role === SERIES_LEAD_ROLE).map((n) => n.id));\n if (leads.size === 0) return canvas;\n const master = nodesWithRole(canvas, STATIC_MASTER_ROLE)[0]?.id ?? \"master\";\n const nodes = canvas.nodes\n .filter((n) => !leads.has(n.id))\n .map((n) => {\n const refs = n.inputs?.reference;\n if (!Array.isArray(refs) || !refs.some((r) => typeof r === \"string\" && leads.has(refNode(r)))) return n;\n const relaid = n.tags?.role === STATIC_ADAPT_FORMAT_ROLE;\n const kept = refs.filter((r) => !(typeof r === \"string\" && leads.has(refNode(r))));\n return { ...n, inputs: { ...n.inputs, reference: relaid ? [`$ref:${master}.image`, ...kept] : kept } };\n });\n return { ...canvas, nodes };\n}\n\nfunction leadPicture(id: string, url: string): NodeDecl {\n return { id, type: \"ingest\", params: { source: \"url\", url, expect: \"image\" }, tags: { role: SERIES_LEAD_ROLE } };\n}\n\n/** The node a `$ref:` names. */\nfunction refNode(ref: string): string {\n return ref.match(/^\\$ref:([a-z][a-z0-9_]*)\\./)?.[1] ?? \"\";\n}\n\ntype VariantStage = { removed: Set<string>; template: NodeDecl; masterId: string; prefilterId?: string };\n\n/** The part of a static canvas that produces the chosen picture, and the master it feeds. */\nfunction variantStage(canvas: Canvas): VariantStage {\n const variants = nodesWithRole(canvas, STATIC_VARIANTS_ROLE);\n const calls = nodesWithRole(canvas, STATIC_VARIANT_CALL_ROLE);\n const template = [...variants, ...calls].find((n) => n.type === \"image_generate\");\n const master = nodesWithRole(canvas, STATIC_MASTER_ROLE)[0];\n if (!template || !master) throw new Error(\"not a static ad canvas: it renders no variants to correct\");\n // The prefilter stays when it feeds the master: it frames the one picture as it frames a variant.\n const masterInputs = JSON.stringify(master.inputs ?? {});\n const prefilter = nodesWithRole(canvas, VARIANT_PREFILTER_ROLE).find((n) => masterInputs.includes(`$ref:${n.id}.`));\n const removed = new Set(\n [\n ...variants,\n ...calls,\n ...nodesWithRole(canvas, VARIANT_PREFILTER_ROLE).filter((n) => n !== prefilter),\n ...nodesWithRole(canvas, STATIC_PICK_ROLE),\n ].map((n) => n.id),\n );\n return {\n removed,\n template: template as unknown as NodeDecl,\n masterId: master.id,\n ...(prefilter ? { prefilterId: prefilter.id } : {}),\n };\n}\n\n/** The canvas with its variant stage replaced by `source` nodes whose `ref` the master now reads. */\nfunction withMasterSource(\n canvas: Canvas,\n stage: VariantStage,\n source: NodeDecl[],\n ref: string,\n opts: { note: string },\n): Canvas {\n // A picture drawn at another shape than the master's is cut where its words stay whole (Dev, Oct\n // 2026: a correction cut about its centre lost the 4:5's headline and button): through the prefilter,\n // which reads them, never asking the judge about one picture.\n const kept = canvas.nodes\n .filter((n) => !stage.removed.has(n.id))\n .map((n) => {\n if (n.id === stage.prefilterId) {\n const { judge_model: _judge, judge_prompt: _prompt, ...params } = n.params ?? {};\n return { ...n, inputs: { ...n.inputs, images: [ref] }, params };\n }\n return n.id === stage.masterId && !stage.prefilterId ? { ...n, inputs: { ...n.inputs, image: ref } } : n;\n });\n const at = kept.findIndex((n) => n.id === (stage.prefilterId ?? stage.masterId));\n const description = `${canvas.metadata?.description ?? \"\"} ${opts.note}`;\n return {\n ...canvas,\n metadata: { ...canvas.metadata, description: description.trim() },\n nodes: [...kept.slice(0, at), ...source, ...kept.slice(at)],\n };\n}\n\ntype StaticAdReport = {\n element_count: number;\n elements: Array<{ label: string; type: string; description: string | null; asset_todo: string }>;\n /** One paragraph on what a run does: candidates, judge, text, logo, formats. */\n summary: string;\n /** The typeface shown to the model, or null when none could be loaded. */\n font: string | null;\n /** Whether the logo is given to the model, and why. */\n logo: { drawn: boolean; why: string };\n /** A reference that pictures nothing but copy: the ad is its words on the brand's colours. */\n text_forward: boolean;\n /** Lines written on something in the scene. */\n in_scene_text: string[];\n /** Always empty: the reference's living heroes are no longer slots to fuse into a sheet (kept for readers of the report). */\n actor_sheets: string[];\n /** The shape the variants render at (the model's nearest to the master's format). */\n aspect_ratio: string;\n /** The placement preset delivered, or null for a single format. */\n placements: AdFormatPlatform | null;\n /** The platforms the sizes came from, or null when they came from a preset. */\n platforms: string[] | null;\n /** Fixed-pixel banners the platforms require that a re-laid-out picture cannot make (`728x90`): made apart. */\n banners: string[];\n /** Every delivered format, each exact. */\n output_formats: string[];\n /** The master and how each size is made from it (the master itself, a cut, or an adapt; never a re-render). */\n format_plan: StaticFormatPlan;\n /** Variants rendered. */\n takes: number;\n /** Whether a judge picks between the variants (more than one). */\n judged: boolean;\n /** What makes the source ad work, to rebuild for our brand rather than lose in the rewrite. */\n winning_mechanisms: WinningMechanism[];\n /** Describe-contract keys the blueprint came back without. */\n describe_gaps: Array<\"fonts\" | \"winning_mechanisms\">;\n};\n\nfunction logoWhy(spec: AdSpec, opts: StaticAdOpts): string {\n if (!opts.brand?.logoPath) {\n return 'src/brand/logos/ has no real logo file, so none is drawn (never a stand-in). Add the file: every run wires it while prompt.json keeps `\"logo\": true`.';\n }\n if (!spec.logo) {\n return 'prompt.json has `\"logo\": false`, so none is drawn: Meta shows the page name and picture on every ad.';\n }\n return logoComposited(opts)\n ? `The brand logo ${opts.brand.logoPath} is placed on every size inside its safe area; the model draws none.`\n : `The brand logo ${opts.brand.logoPath} is given to the model as a reference to reproduce where the concept places it, never invented.`;\n}\n\n/** Human-facing checklist the command prints — the real assets to supply before running. */\nexport function staticAdReport(input: unknown, elementsInput: unknown, opts: StaticAdOpts): StaticAdReport {\n const blueprint = Blueprint.parse(input);\n const elements = MainElements.parse(elementsInput);\n const plan = planStaticFormats(opts.genModel, outputFormats(blueprint, opts), {\n masterShape: opts.masterShape ?? DEFAULT_MASTER_SHAPE,\n ...(safePlacements(opts) ? { placements: safePlacements(opts) } : {}),\n });\n const spec = specFor(input, elements, opts);\n const drawLogo = logoDrawn(spec, opts);\n const takes = takesFor(opts);\n const inScene = inSceneText(blueprint).map((t) => t.text);\n return {\n element_count: elements.length,\n elements: assignElementLabels(elements, \"static\").map(({ el, label }) => ({\n label,\n type: el.type,\n description: el.description ?? null,\n asset_todo: suppliedImage(el)\n ? \"none: cast from a company Avatar's identity sheet\"\n : el.type.toLowerCase() === \"person\"\n ? \"description-only casting: prefer a suitable ready company Avatar via --avatar, including for a single ad\"\n : el.type.toLowerCase() === \"logo\"\n ? drawLogo\n ? `none: the brand's own logo ${opts.brand?.logoPath} is given to the model, never the reference's`\n : \"none: no logo is drawn (the logo is optional)\"\n : \"none: described in words, never the reference's own; to show ours, add a reference with its `image` in prompt.json\",\n })),\n summary: describeRun({\n takes,\n logo: drawLogo ? (logoComposited(opts) ? \"composite\" : \"model\") : null,\n font: opts.brand?.font,\n copy: copyStrings(blueprint).length,\n inScene: inScene.length,\n plan,\n }),\n font: opts.brand?.font ? fontLabel(opts.brand.font) : null,\n logo: { drawn: drawLogo, why: logoWhy(spec, opts) },\n text_forward: isTextForward(blueprint),\n in_scene_text: inScene,\n actor_sheets: [],\n aspect_ratio: plan.master.renderRatio,\n placements: opts.placements ?? null,\n platforms: opts.platforms?.length ? [...opts.platforms] : null,\n banners: opts.platforms?.length ? deliverySet(opts.platforms).banners.map((b) => b.format) : [],\n output_formats: plan.sizes.map((f) => f.format),\n format_plan: plan,\n takes,\n judged: takes > 1,\n winning_mechanisms: winningMechanisms(blueprint),\n describe_gaps: describeGaps(blueprint),\n };\n}\n","/** A company Avatar resolved for casting: the identity sheet a slot grounds on, and the words it is described with. */\nexport type CastAvatar = { sheetUrl: string; subjectDescription?: string; handle?: string };\n\n/**\n * The fields a deconstruct records about who a person IS (not what they do or wear in\n * this ad). They describe the source ad's person, so a cast Avatar replaces them.\n */\nconst SOURCE_IDENTITY_FIELDS = [\n \"perceived_age_band\",\n \"perceived_gender_presentation\",\n \"perceived_ethnicity_or_skin_tone\",\n \"hair\",\n \"facial_hair\",\n \"build_body_type\",\n \"distinguishing_features\",\n] as const;\n\n/**\n * Rewrite a blueprint's person entry for the Avatar cast in their place: the Avatar's\n * words, verbatim, and none of the source person's face or body. That entry reaches the\n * render as the target blueprint, and a sheet and words that describe two people render\n * a third. In place — callers pass their own clone.\n */\nexport function recastPersonEntry(entry: Record<string, unknown>, avatarDescription: string): void {\n for (const field of SOURCE_IDENTITY_FIELDS) delete entry[field];\n entry.description = avatarDescription;\n}\n\ntype CastableElement = { type: string; label?: string; same_as?: string | null };\n\n/**\n * How a scaffold casts: whether a `same_as` look of a cast person is that person too (a\n * video's recurring cast can change clothes; a static ad's selection never links looks),\n * and whether the slot is told its picture is already a turnaround (a video builds a sheet\n * from any other picture, so it must be told; a static ad never sheets a person).\n */\ntype CastingRules = { looks: boolean; markSheet: boolean };\n\nconst castsSlot = (el: CastableElement, rules: CastingRules): boolean =>\n el.type.toLowerCase() === \"person\" && !(rules.looks && el.same_as);\n\n/**\n * Cast company Avatars into the person slots, in order: the first person gets the first\n * avatar, and so on. The slot takes the avatar's identity sheet as its picture and its\n * subject description as the words, so the render shows the brand's own cast. With `looks`,\n * a `same_as` look of a cast person grounds on that person's sheet too — it is the same face\n * in other clothes. A person left without an avatar keeps its TODO: casting nobody is how a\n * canvas renders a stranger.\n */\nexport function castAvatarsInOrder<T extends CastableElement>(\n elements: readonly T[],\n avatars: readonly CastAvatar[],\n rules: CastingRules,\n): T[] {\n const sheet = (avatar: CastAvatar) => ({\n source_image: avatar.sheetUrl,\n ...(avatar.handle ? { cast_handle: avatar.handle } : {}),\n ...(rules.markSheet ? { source_is_sheet: true } : {}),\n });\n const byLabel = new Map<string, CastAvatar>();\n let next = 0;\n const cast = elements.map((el) => {\n const avatar = castsSlot(el, rules) ? avatars[next] : undefined;\n if (!avatar) return el;\n next += 1;\n if (el.label) byLabel.set(el.label, avatar);\n const description = avatar.subjectDescription?.trim();\n return { ...el, ...(description ? { description } : {}), ...sheet(avatar) };\n });\n if (!rules.looks) return cast;\n return cast.map((el) => {\n const avatar = el.same_as ? byLabel.get(el.same_as) : undefined;\n return avatar ? { ...el, ...sheet(avatar) } : el;\n });\n}\n\n/**\n * The avatar handles `castAvatarsInOrder` had no person for, in the order given. A\n * selection can hold fewer people than were cast — a one-scene presenter the video\n * selection drops as non-recurring, a second handle on a one-person ad — and an uncast\n * avatar must be said, not assumed.\n */\nexport function avatarsLeftUncast(\n elements: readonly CastableElement[],\n handles: readonly string[],\n rules: CastingRules,\n): string[] {\n return handles.slice(elements.filter((el) => castsSlot(el, rules)).length);\n}\n\n/** A text's words that carry meaning, folded: accents off, short words out. */\nfunction meaningWords(text: string): Set<string> {\n return new Set(\n text\n .normalize(\"NFD\")\n .replace(/\\p{M}/gu, \"\")\n .toLowerCase()\n .split(/[^\\p{L}\\p{N}]+/u)\n .filter((w) => w.length >= 4 || /^\\d{2}$/.test(w))\n .map((w) => (w.length > 5 ? w.slice(0, 5) : w)),\n );\n}\n\n/**\n * The avatars in the order they should be cast for `persona` (who the ad is for, in words):\n * the best match first, by the words their descriptions share with it (D2: casting followed the\n * order the handles were passed in, not who the ad is for); a tie keeps the given order. Pure.\n */\nexport function avatarsByPersona<T extends CastAvatar>(avatars: readonly T[], persona: string | undefined): T[] {\n if (!persona?.trim()) return [...avatars];\n const wanted = meaningWords(persona);\n const score = (avatar: T) => [...meaningWords(avatar.subjectDescription ?? \"\")].filter((w) => wanted.has(w)).length;\n return avatars\n .map((avatar, index) => ({ avatar, index, score: score(avatar) }))\n .sort((a, b) => b.score - a.score || a.index - b.index)\n .map(({ avatar }) => avatar);\n}\n","import { readdir, readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport sharp from \"sharp\";\n\n/**\n * The client's brand, read from the workspace instead of asked for.\n *\n * `scaffold-ad` takes `brand.logo` and `brand.palette` in the spec, and the spec is\n * written by an agent. Asked three times for an ad \"with our logo\", it supplied one\n * once: two of the three ads rendered with no mark anywhere and no closing card, which\n * is not a model being careless so much as a design that made the correct outcome\n * depend on somebody remembering.\n *\n * Both values already exist at a fixed place in every workspace — `src/brand/BRAND.md`\n * and `src/brand/logos/`. Reading them is deterministic, costs nothing, and cannot be\n * forgotten. The spec still wins when it says something: this fills the silence.\n *\n * The same move that fixed the voice. A Spanish ad was cast from English stock voices\n * because nobody declared a language; reading it off the script removed the whole class\n * of failure rather than adding another reminder.\n */\n\n/** Where a workspace keeps its brand. Fixed by the scaffold, not by convention. */\nexport const BRAND_DIR = \"src/brand\";\n\n/**\n * Brand hexes in the order the document introduces them.\n *\n * Order matters: the first is used as the closing plate and the CTA fill, and a brand\n * document leads with its primary. Deduplicated case-insensitively so `#1F6F43` and\n * `#1f6f43` are one colour, and near-white/near-black are dropped — they are page\n * furniture in every brand doc and make a terrible plate.\n */\nexport function paletteFromBrandDoc(markdown: string): string[] {\n const seen = new Set<string>();\n const out: string[] = [];\n for (const match of markdown.matchAll(/#([0-9a-fA-F]{6})\\b/g)) {\n const hex = `#${(match[1] as string).toLowerCase()}`;\n if (seen.has(hex)) continue;\n seen.add(hex);\n const luma = hexLuma(hex);\n if (luma === null || luma > 236 || luma < 14) continue;\n out.push(hex);\n }\n return out;\n}\n\n/** How light a `#rrggbb` is, on the same 0–255 scale as `inkLuma`. Null if it is not one. */\nexport function hexLuma(hex: string): number | null {\n const m = hex.trim().match(/^#?([0-9a-fA-F]{6})$/);\n if (!m) return null;\n const v = m[1] as string;\n const r = Number.parseInt(v.slice(0, 2), 16);\n const g = Number.parseInt(v.slice(2, 4), 16);\n const b = Number.parseInt(v.slice(4, 6), 16);\n return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n}\n\n/** The first `#rrggbb` a palette actually holds, normalised, or null when it holds none. */\nexport function firstHex(palette: string[] | undefined): string | null {\n const hex = (palette ?? []).find((h) => /^#?[0-9a-fA-F]{6}$/.test(h.trim()));\n if (!hex) return null;\n const trimmed = hex.trim();\n return trimmed.startsWith(\"#\") ? trimmed : `#${trimmed}`;\n}\n\n/**\n * Whether the closing PLATE is dark, which is what the card's own colours are chosen\n * against.\n *\n * The engine fills that plate with the palette's dominant hex, and with **no palette it\n * fills it black** (`brandPlateColor`, engine/scaffold/video/routes/brand-card.ts) — so \"nothing to go on\"\n * means dark here, not light. Getting that backwards is the whole failure this answers: the\n * card's button carried a fixed near-black fill, which on a black or near-black plate is a\n * button nobody can see.\n */\nexport function plateIsDark(palette: string[] | undefined): boolean {\n const luma = hexLuma(firstHex(palette) ?? \"\");\n return luma === null || luma < PLATE_DARK_LUMA;\n}\n\n/**\n * Below this a plate takes the light button, above it the near-black one.\n *\n * Set at the midpoint rather than at a contrast-ratio threshold, because the choice is\n * between two opposites and one of them always wins: measured against the brand green this\n * was reported on (`#1d5d47`, luma 78), white-on-green scores 7.8:1 and black-on-green\n * 2.7:1, so the light button is not merely safe there, it is the better of the two.\n */\nconst PLATE_DARK_LUMA = 140;\n\n/**\n * Names that mean \"this is the variant for some OTHER context\", in both languages the\n * client repos are written in.\n *\n * `dark` / `light` and their Spanish equivalents are the ones this list was missing, and\n * the omission chose the wrong file for a real client: `\\blogo\\b` matches `logo-dark.svg`\n * exactly as well as `logo.svg`, so the two tied and the tie went alphabetically —\n * `logo-dark.svg` first, because `-` sorts before `.`. Cubicup's correct mark was sitting\n * beside it, and the ad went out wearing the one drawn for a dark background.\n *\n * `dark`/`light` name the BACKGROUND the variant is for in most kits and the ink itself in\n * some, which is exactly why neither belongs in the default pick: whichever it means, it is\n * a variant, and the plain name beside it is the mark.\n */\nconst MARK_VARIANT_NAMES =\n /\\b(mono|white|black|blanco|negro|negra|dark|light|oscuro|oscura|claro|clara|negativo|negativa|positivo|inverse|inverso|inversa|inv|reverse|reversed|alt|outline|transparent|watermark|icon|favicon|mark-only|isotipo|isotype)\\b/;\n\n/**\n * The file most likely to BE the mark, out of whatever the logos folder holds.\n *\n * A plain \"logo\"-ish name beats a variant (mono, white, dark, icon, favicon), because a\n * variant is for a context this is not — and a raster takes the tie over a vector of the\n * same mark, for the reason spelled out below.\n */\nexport function pickBrandMark(files: string[]): string | null {\n const usable = files.filter((f) => /\\.(svg|png|webp)$/i.test(f));\n if (usable.length === 0) return null;\n const score = (f: string): number => {\n const name = f.toLowerCase();\n let s = 0;\n // A raster wins a tie with a vector of the same mark. The vector used to win outright,\n // for staying crisp at any size — right for a mark made of outlines, ruinous for one\n // whose wordmark is live `<text>`: the render browser has no brand font, substitutes\n // glyphs, and a closing card came back reading \"ChEENLEVI\" while the PNG next to it\n // would have been exact. A raster cannot be wrong about its own letterforms.\n if (/\\.(png|webp)$/.test(name)) s += 1;\n if (/\\b(logo|wordmark|lockup)\\b/.test(name)) s += 2;\n if (MARK_VARIANT_NAMES.test(name)) s -= 3;\n return s;\n };\n // Ties break on the SHORTER name, not alphabetically. A variant's name is its base plus\n // a qualifier, so the shorter of two equally-scored files is the plainer mark — and\n // alphabetical order actively preferred the qualified one, since `logo-x.svg` sorts\n // before `logo.svg`. This is the net that catches a qualifier nobody listed above.\n return [...usable].sort((a, b) => score(b) - score(a) || a.length - b.length || a.localeCompare(b))[0] ?? null;\n}\n\n/** What the workspace can supply on its own, for the fields a spec left empty. */\nexport type BrandFromWorkspace = { logo?: string; palette?: string[] };\n\n/**\n * Read the brand out of the workspace. Never throws — a workspace with no brand yet is\n * an ordinary state, and the command says so in its hints rather than failing.\n */\nexport async function readBrandFromWorkspace(root = \".\"): Promise<BrandFromWorkspace> {\n const found: BrandFromWorkspace = {};\n\n const doc = await readFile(path.join(root, BRAND_DIR, \"BRAND.md\"), \"utf-8\").catch(() => null);\n if (doc) {\n const palette = paletteFromBrandDoc(doc);\n if (palette.length > 0) found.palette = palette;\n }\n\n const dir = path.join(root, BRAND_DIR, \"logos\");\n const files = await readdir(dir).catch(() => null);\n if (files) {\n const mark = pickBrandMark(files);\n if (mark) found.logo = path.join(BRAND_DIR, \"logos\", mark);\n }\n\n return found;\n}\n\n/**\n * Whether an SVG mark is live TEXT rather than outlines.\n *\n * A real brand mark ships as paths: outlined type renders identically on every machine,\n * and that is the whole point of a logo. An SVG that draws its wordmark with `<text>`\n * depends on a font being installed wherever it is rendered, and is almost always a\n * stand-in somebody typed out.\n *\n * The one that reached a client repo did both: `<text>greenleaf</text>` in a font the\n * renderer did not have, with a decorative leaf parked on top of the last letter — so\n * every ad it appeared in read \"greenleá\". Existence was not enough to check; the file\n * was there, and it was still not the company's logo.\n */\nexport function svgIsLiveText(svg: string): boolean {\n return /<text[\\s>]/i.test(svg);\n}\n\n/**\n * Whether the mark this ad is about to wear can be trusted.\n *\n * Asked of the DIRECTORY, not of the file the spec happens to point at. The earlier gate\n * inspected only the resolved `.svg`, so a PNG rasterised from a typed stand-in walked\n * past it — and the refusal's own advice (\"or a PNG at 1000px or wider\") is what pushed an\n * agent to produce one. Rasterised on a machine without the brand font, it bakes the\n * substituted glyphs into pixels, where no check can read them back: a closing card came\n * out reading \"ChEENLEVI\" and nothing in the pipeline could tell.\n *\n * So the question is about the source material. A raster is only trusted when the marks it\n * could have come from are trustworthy: with a typed SVG sitting beside it, it is far more\n * likely to be a render of that than an asset the client sent.\n *\n * Asked of the workspace's OWN brand folder first, because that folder is what vouches for\n * anything derived from it. Narrowing this to \"the folder the chosen file happens to sit\n * in\" is how a rasterised stand-in got through: the agent exported the typed SVG to a PNG\n * inside its own run directory, where nothing sat beside it to give it away, and the\n * closing card came out wearing #1D5D47 — the stand-in's own fill — instead of the logo's\n * real green. A raster cannot be read back, so the question has to be asked upstream of it.\n *\n * With no brand folder, the file's own folder is all there is to go on, and that is fine:\n * a spec may point outside the workspace, and neither case is evidence of a stand-in.\n */\nexport async function markIsReal(markPath: string, root = \".\"): Promise<boolean> {\n const brandLogos = await findBrandLogosDir(markPath, root);\n if (brandLogos) return await markDirectoryIsReal(brandLogos);\n return await markDirectoryIsReal(path.dirname(markPath));\n}\n\n/**\n * The brand folder this mark belongs to, found by walking UP from the mark itself.\n *\n * Keyed to the caller's cwd once, and that is all it took: run from `src/creatives/<slug>/`\n * with an absolute logo path — exactly what the sandbox agent produces — and the audit\n * looked for `src/brand/logos` under THAT directory, found nothing, and fell back to\n * judging the file by its own neighbours. A raster of the typed stand-in sat alone in a run\n * folder with nothing beside it to give it away, so the whole gate answered `ok: true`.\n *\n * The cwd is still consulted, because a mark can legitimately live outside the workspace;\n * it is simply no longer the only place looked.\n */\nasync function findBrandLogosDir(markPath: string, root: string): Promise<string | null> {\n const candidates: string[] = [];\n let dir = path.resolve(path.dirname(markPath));\n for (;;) {\n candidates.push(path.join(dir, BRAND_DIR, \"logos\"));\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n candidates.push(path.join(root, BRAND_DIR, \"logos\"));\n for (const candidate of candidates) {\n if (\n await readdir(candidate).then(\n () => true,\n () => false,\n )\n )\n return candidate;\n }\n return null;\n}\n\nexport async function markDirectoryIsReal(dir: string): Promise<boolean> {\n const files = await readdir(dir).catch(() => null);\n if (!files) return false;\n const marks = files.filter((f) => /\\.(svg|png|webp)$/i.test(f));\n if (marks.length === 0) return false;\n for (const f of marks.filter((f) => /\\.svg$/i.test(f))) {\n const svg = await readFile(path.join(dir, f), \"utf-8\").catch(() => null);\n // Unreadable is not clean: a mark nobody can open cannot vouch for the rasters beside it.\n if (svg === null || svgIsLiveText(svg)) return false;\n }\n return true;\n}\n\n/**\n * Whether a mark's own INK is light — a white or near-white logo, drawn to sit on colour.\n *\n * Asked because the layer that draws a mark has to put something behind it, and a panel\n * that fits one mark erases the other. The brand layer used to back every mark with a\n * near-white panel, which is right for the usual case (a mark is designed against white)\n * and ruinous for a light one: a client's `logo-dark.svg` — white ink, made for a dark\n * background — rendered as an empty white box in the corner and again, 300px tall, in the\n * middle of the closing card. White on white cannot be fixed by anything downstream.\n *\n * Measured off the pixels rather than guessed from the filename. `logo-dark` names the\n * BACKGROUND it is for in one kit and the ink itself in the next, so the name is not\n * evidence; the file is.\n *\n * Only the ink counts. A mark is mostly transparent — a logo on a transparent canvas is\n * 80% nothing — so averaging every pixel measures the emptiness around it and calls\n * everything dark. Pixels under `INK_ALPHA` are dropped before the average.\n */\nconst INK_ALPHA = 128;\n\n/**\n * The mean luminance of the opaque pixels in an RGBA buffer, or null when there are none.\n *\n * Pure, and exported for tests: the decision this feeds is one threshold on one number,\n * and a number is worth testing without a file on disk.\n */\nexport function inkLuma(rgba: Uint8Array | Buffer): number | null {\n let sum = 0;\n let count = 0;\n for (let i = 0; i + 3 < rgba.length; i += 4) {\n if ((rgba[i + 3] as number) < INK_ALPHA) continue;\n sum += 0.2126 * (rgba[i] as number) + 0.7152 * (rgba[i + 1] as number) + 0.0722 * (rgba[i + 2] as number);\n count += 1;\n }\n return count === 0 ? null : sum / count;\n}\n\n/**\n * Above this the ink is light enough that a near-white panel would swallow it.\n *\n * Deliberately high: a dark panel behind a mid-tone mark is the worse mistake of the two,\n * because a mid-tone mark reads on white and most marks are mid-tone or darker.\n */\nconst LIGHT_INK_LUMA = 170;\n\n/**\n * Whether the mark at `markPath` is light-inked. False for anything unreadable — the\n * near-white panel is the right default, and a mark this cannot open is not a reason to\n * change the backing of every ad.\n */\nexport async function markInkIsLight(markPath: string): Promise<boolean> {\n try {\n // Downscaled first: the decision is one average, and a 4000px wordmark is 64MB of\n // pixels to compute it from. `density` is what makes an SVG rasterise at a usable\n // size — sharp renders one at 72dpi by default, which for a small viewBox is a\n // handful of pixels to average.\n const { data } = await sharp(markPath, { density: 144 })\n .resize(96, 96, { fit: \"inside\", withoutEnlargement: true })\n .ensureAlpha()\n .raw()\n .toBuffer({ resolveWithObject: true });\n const luma = inkLuma(data);\n return luma !== null && luma > LIGHT_INK_LUMA;\n } catch {\n return false;\n }\n}\n","/**\n * The face a static ad's copy is set in, when the brand has no font FILE.\n *\n * A brand usually NAMES its typeface even without the file: in tokens.json, or in\n * BRAND.md, read in any language by the brand reader (a face it bans is never used). A named family is fetched as a web font file the render\n * stages like any other font. A brand that names nothing gets no face from here.\n */\n\nimport { brandTokensSchema } from \"@baker/api\";\nimport type { BrandDocReading } from \"../brand/brand-doc.ts\";\nimport { type BrandFontFace, GENERIC_FAMILIES, type ProviderDeps, resolveBrandFaces } from \"../brand/fonts.ts\";\n\nexport type FontOrigin = \"brand_file\" | \"tokens\" | \"brand_md\";\ntype Named = { family: string; origin: Extract<FontOrigin, \"tokens\" | \"brand_md\"> };\ntype NamedTypefaces = { headline?: Named; body?: Named };\n\n/** Where a face comes from: a file in the workspace, or a web font file by URL. */\nexport type FontSlot = { path: string } | { url: string };\n\n/** One role's face, and what the report says about it. */\nexport type ResolvedFont = { slot: FontSlot; family: string | null; origin: FontOrigin };\n\n/** The weight a headline face is fetched at. */\nconst HEADLINE_WEIGHT = 800;\n\n/** The faces the brand's documents name and use, by role; a banned face is never one. Pure. */\nfunction fromReading(reading: BrandDocReading): NamedTypefaces {\n const used = reading.fonts.filter((f) => f.use && !GENERIC_FAMILIES.has(f.family.toLowerCase()));\n const named = (family: string | undefined): Named | undefined =>\n family ? { family, origin: \"brand_md\" } : undefined;\n const loose = used.filter((f) => f.role === \"any\").map((f) => f.family);\n const headline = named(used.find((f) => f.role === \"heading\")?.family ?? loose[0]);\n const body = named(used.find((f) => f.role === \"body\")?.family ?? loose[1] ?? loose[0]);\n return { ...(headline ? { headline } : {}), ...(body ? { body } : {}) };\n}\n\nfunction fromTokens(tokensJson: string): NamedTypefaces {\n let json: unknown;\n try {\n json = JSON.parse(tokensJson);\n } catch {\n return {};\n }\n const parsed = brandTokensSchema.safeParse(json);\n if (!parsed.success) return {};\n const heading = parsed.data.fonts.find((f) => f.role === \"heading\")?.family;\n const body = parsed.data.fonts.find((f) => f.role === \"body\")?.family;\n return {\n ...(heading ? { headline: { family: heading, origin: \"tokens\" as const } } : {}),\n ...(body ? { body: { family: body, origin: \"tokens\" as const } } : {}),\n };\n}\n\n/**\n * The heading and body families the brand names: tokens.json first (the canonical\n * machine source), then what BRAND.md states, as the brand reader read it in\n * whatever language it is written (`brand-doc.ts`). Pure.\n */\nexport function namedTypefaces(sources: {\n tokensJson?: string | null;\n brandDoc?: BrandDocReading | null;\n}): NamedTypefaces {\n const tokens = sources.tokensJson ? fromTokens(sources.tokensJson) : {};\n const doc = sources.brandDoc ? fromReading(sources.brandDoc) : {};\n const headline = tokens.headline ?? doc.headline;\n const body = tokens.body ?? doc.body;\n return { ...(headline ? { headline } : {}), ...(body ? { body } : {}) };\n}\n\n/** The upright latin face nearest `weight` that a catalogue serves. */\nfunction nearest(faces: BrandFontFace[], weight: number): BrandFontFace | undefined {\n return [...faces]\n .filter((f) => f.style === \"normal\")\n .sort((a, b) => Math.abs(a.weight - weight) - Math.abs(b.weight - weight))[0];\n}\n\n/**\n * A web font FILE for a family at a weight, from Google Fonts or Fontsource, or null when\n * neither serves it (or neither answered). A weight the family lacks takes its nearest.\n */\nexport async function webFontUrl(family: string, weight: number, deps: ProviderDeps): Promise<string | null> {\n const first = await resolveBrandFaces(family, [weight], [\"latin\"], deps);\n if (first.ok) return nearest(first.faces, weight)?.url ?? null;\n if (first.reason !== \"weight\" || first.available.length === 0) return null;\n const closest = [...first.available].sort((a, b) => Math.abs(a - weight) - Math.abs(b - weight))[0];\n if (closest === undefined) return null;\n const retry = await resolveBrandFaces(family, [closest], [\"latin\"], deps);\n return retry.ok ? (nearest(retry.faces, closest)?.url ?? null) : null;\n}\n\ntype ResolveUrl = (family: string, weight: number) => Promise<string | null>;\n\nconst fileFace = (p: string): ResolvedFont => ({ slot: { path: p }, family: null, origin: \"brand_file\" });\n\n/**\n * The brand's OWN headline face: its font file, else the family it names (fetched as a web\n * font). Null when it has neither: a surface with a look of its own keeps it rather than\n * trading it for a face the brand never chose.\n */\nexport async function brandHeadlineFont(args: {\n file?: string;\n named: NamedTypefaces;\n resolveUrl: ResolveUrl;\n}): Promise<ResolvedFont | null> {\n if (args.file) return fileFace(args.file);\n const named = args.named.headline ?? args.named.body;\n if (!named) return null;\n const url = await args.resolveUrl(named.family, HEADLINE_WEIGHT);\n return url ? { slot: { url }, family: named.family, origin: named.origin } : null;\n}\n","import { readdir, readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { brandTokensSchema } from \"@baker/api\";\nimport { type BrandDocReading, LOGO_FILE, type ReadBrandDocs, readWorkspaceBrandDoc } from \"../brand/brand-doc.ts\";\nimport { BRAND_DIR, markIsReal, paletteFromBrandDoc, pickBrandMark } from \"./ad-brand-source.ts\";\nimport { namedTypefaces } from \"./static-fonts.ts\";\n\n/**\n * What a static ad draws with, read from the workspace's own brand folder.\n *\n * The render browser has no brand font unless it is handed one, and the brand's fonts\n * and logo already sit in `src/brand/`. `tokens.json` is the canonical source (it names\n * which face is the heading and which the body, and which colour is the CTA); a workspace\n * that predates it falls back to the fonts folder, BRAND.md's hexes, and the logos folder.\n * What only prose states (the brand's name, the words its logo prints, a colour it bans) is\n * read from BRAND.md and COMPANY.md by the brand reader, in any language (`brand-doc.ts`).\n * Paths come back absolute. Never throws: a workspace with no brand yet is ordinary.\n */\ntype StaticBrandKit = {\n /** The brand's name, as BRAND.md or COMPANY.md states it: every check that reads an ad needs it. */\n name?: string;\n /** The words the brand's logo prints: BRAND.md's wordmark, and a product a logo file is named after. */\n lockup?: string[];\n logo?: string;\n headlineFont?: string;\n bodyFont?: string;\n palette: string[];\n ctaColor?: string;\n /** The colours tokens.json gives a role: the page ground, the ink on it, the accent. */\n colors?: { background?: string; foreground?: string; accent?: string };\n};\n\n/**\n * The brand's colours as the render prompt binds them: the page ground, the ink, the\n * accent and the call to action. Written into prompt.json as `brand`, so the image model\n * reads the brand as a fact of the blueprint and never as a colour it may borrow from the\n * reference ad. Null when the workspace names no colour at all. Pure.\n */\nexport function brandBlock(kit: Pick<StaticBrandKit, \"palette\" | \"ctaColor\" | \"colors\" | \"name\" | \"lockup\">): {\n name?: string;\n lockup?: string[];\n ground?: string;\n ink?: string;\n accent?: string;\n cta?: string;\n palette: string[];\n} | null {\n const palette = [...new Set([...(kit.palette ?? []), ...(kit.ctaColor ? [kit.ctaColor] : [])])];\n const block = {\n ...(kit.name ? { name: kit.name } : {}),\n ...(kit.lockup && kit.lockup.length > 0 ? { lockup: kit.lockup } : {}),\n ...(kit.colors?.background ? { ground: kit.colors.background } : {}),\n ...(kit.colors?.foreground ? { ink: kit.colors.foreground } : {}),\n ...(kit.colors?.accent ? { accent: kit.colors.accent } : {}),\n ...(kit.ctaColor ? { cta: kit.ctaColor } : {}),\n palette,\n };\n return palette.length === 0 && Object.keys(block).length === 1 ? null : block;\n}\n\nconst FONT_FILE = /\\.(woff2|woff|ttf|otf)$/i;\nconst HEAVY = /(black|heavy|extra-?bold|bold|semi-?bold|[6-9]00)/i;\nconst REGULAR = /(regular|book|normal|roman|[^0-9]400)/i;\n\n/** The face for a role: the boldest of its files for headlines, the regular one for body. */\nfunction pickFace(files: string[], want: \"heavy\" | \"regular\"): string | undefined {\n if (files.length === 0) return undefined;\n const pattern = want === \"heavy\" ? HEAVY : REGULAR;\n return files.find((f) => pattern.test(path.basename(f))) ?? files[0];\n}\n\nfunction exists(file: string): Promise<boolean> {\n return readFile(file).then(\n () => true,\n () => false,\n );\n}\n\nasync function fromTokens(root: string): Promise<Partial<StaticBrandKit>> {\n const raw = await readFile(path.join(root, BRAND_DIR, \"tokens.json\"), \"utf8\").catch(() => null);\n if (!raw) return {};\n let json: unknown;\n try {\n json = JSON.parse(raw);\n } catch {\n return {};\n }\n const parsed = brandTokensSchema.safeParse(json);\n if (!parsed.success) return {};\n const tokens = parsed.data;\n const filesFor = async (role: \"heading\" | \"body\"): Promise<string[]> => {\n const out: string[] = [];\n for (const font of tokens.fonts.filter((f) => f.role === role)) {\n for (const file of font.files ?? []) {\n const abs = path.join(root, file);\n if (FONT_FILE.test(file) && (await exists(abs))) out.push(abs);\n }\n }\n return out;\n };\n const heading = await filesFor(\"heading\");\n const body = await filesFor(\"body\");\n const logos: string[] = [];\n for (const logo of tokens.logos) {\n if (await exists(path.join(root, logo.path))) logos.push(logo.path);\n }\n const mark = pickBrandMark(logos.map((l) => path.basename(l)));\n const markPath = mark ? logos.find((l) => path.basename(l) === mark) : undefined;\n return {\n headlineFont: pickFace(heading, \"heavy\") ?? pickFace(body, \"heavy\"),\n bodyFont: pickFace(body, \"regular\") ?? pickFace(heading, \"regular\"),\n palette: tokens.colors\n .filter((c) => c.role === \"cta\" || c.role === \"accent\" || c.role === \"other\")\n .map((c) => c.value.slice(0, 7).toLowerCase()),\n ctaColor: tokens.colors\n .find((c) => c.role === \"cta\")\n ?.value.slice(0, 7)\n .toLowerCase(),\n colors: Object.fromEntries(\n ([\"background\", \"foreground\", \"accent\"] as const).flatMap((role) => {\n const hex = tokens.colors\n .find((c) => c.role === role)\n ?.value.slice(0, 7)\n .toLowerCase();\n return hex ? [[role, hex]] : [];\n }),\n ),\n ...(markPath ? { logo: path.join(root, markPath) } : {}),\n };\n}\n\n/** The brand's mark: the one tokens.json names, else the best file in the logos folder; never a typed stand-in. */\nasync function realLogo(root: string, named: string | undefined): Promise<string | undefined> {\n let logo = named;\n if (!logo) {\n const logosDir = path.join(root, BRAND_DIR, \"logos\");\n const mark = pickBrandMark(\n ((await readdir(logosDir).catch(() => [])) as string[]).filter((f) => !f.startsWith(\".\")),\n );\n if (mark) logo = path.join(logosDir, mark);\n }\n // A typed SVG stand-in (a wordmark set in <text>) is not the company's logo: leave the slot open.\n return logo && (await markIsReal(logo, root)) ? logo : undefined;\n}\n\n/** The palette a brand document gives, without any colour it bans. Pure. */\nfunction documentPalette(doc: string | null, reading: BrandDocReading | null): string[] {\n const banned = new Set((reading?.colors ?? []).filter((c) => !c.use).map((c) => c.hex));\n return doc ? paletteFromBrandDoc(doc).filter((hex) => !banned.has(hex)) : [];\n}\n\nexport async function readStaticBrandKit(\n root = \".\",\n opts: { readBrandDoc?: ReadBrandDocs } = {},\n): Promise<StaticBrandKit> {\n const tokens = await fromTokens(root);\n const fontsDir = path.join(root, BRAND_DIR, \"fonts\");\n const fontFiles = ((await readdir(fontsDir).catch(() => [])) as string[])\n .filter((f) => FONT_FILE.test(f))\n .sort()\n .map((f) => path.join(fontsDir, f));\n const headlineFont = tokens.headlineFont ?? pickFace(fontFiles, \"heavy\");\n const bodyFont = tokens.bodyFont ?? pickFace(fontFiles, \"regular\") ?? headlineFont;\n\n const doc = await readFile(path.join(root, BRAND_DIR, \"BRAND.md\"), \"utf8\").catch(() => null);\n const reading = await readWorkspaceBrandDoc(root, opts.readBrandDoc ? { read: opts.readBrandDoc } : {});\n let palette = tokens.palette ?? [];\n if (palette.length === 0) palette = documentPalette(doc, reading);\n const name = reading?.name;\n const lockup = reading?.wordmark ?? [];\n\n const logo = await realLogo(root, tokens.logo);\n\n return {\n ...(name ? { name } : {}),\n ...(lockup.length > 0 ? { lockup } : {}),\n ...(logo ? { logo } : {}),\n ...(headlineFont ? { headlineFont } : {}),\n ...(bodyFont ? { bodyFont } : {}),\n palette,\n ...(tokens.ctaColor ? { ctaColor: tokens.ctaColor } : {}),\n ...(tokens.colors && Object.keys(tokens.colors).length > 0 ? { colors: tokens.colors } : {}),\n };\n}\n\n/** The typefaces the workspace's brand names, in tokens.json or BRAND.md. Never throws. */\nexport async function readNamedTypefaces(\n root = \".\",\n opts: { readBrandDoc?: ReadBrandDocs } = {},\n): Promise<ReturnType<typeof namedTypefaces>> {\n const tokensJson = await readFile(path.join(root, BRAND_DIR, \"tokens.json\"), \"utf8\").catch(() => null);\n const tokens = namedTypefaces({ tokensJson });\n // tokens.json naming both roles is the whole answer: the documents are not read.\n if (tokens.headline && tokens.body) return tokens;\n const brandDoc = await readWorkspaceBrandDoc(root, opts.readBrandDoc ? { read: opts.readBrandDoc } : {});\n return namedTypefaces({ tokensJson, brandDoc });\n}\n\nconst MARK_FILE = /isotype|isologo|symbol|logomark|brandmark|(^|[-_ ])(mark|icon|avatar)([-_ .]|$)/i;\n\n/**\n * Every variant of the brand's own logo in src/brand/logos (light and dark, lockup and wordmark),\n * and its square mark when a file is one: a native screen picks the variant that reads on it, and\n * the mark is its avatar. A file is the brand's when its name says logo or carries the brand's\n * name; a tool's or partner's icon (`powerbi-icon.png`) is not. Never throws.\n */\nexport async function readBrandLogoFiles(\n root: string,\n brandName?: string,\n): Promise<{ logos: string[]; mark?: string }> {\n const dir = path.join(root, BRAND_DIR, \"logos\");\n const files = ((await readdir(dir).catch(() => [])) as string[])\n .filter((f) => /\\.(png|jpe?g|webp|svg)$/i.test(f))\n .sort();\n const slug = (brandName ?? \"\")\n .normalize(\"NFD\")\n .replace(/[\\u0300-\\u036f]/g, \"\")\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"\");\n const ours = (f: string) =>\n LOGO_FILE.test(f) ||\n (slug.length > 2 &&\n f\n .toLowerCase()\n .replace(/[^a-z0-9]/g, \"\")\n .includes(slug));\n const real: string[] = [];\n for (const f of files.filter(ours)) {\n const full = path.join(dir, f);\n if (await markIsReal(full, root)) real.push(full);\n }\n const mark = real.find((f) => MARK_FILE.test(path.basename(f)));\n const logos = real.filter((f) => f !== mark);\n return { logos: logos.length > 0 ? logos : real, ...(mark ? { mark } : {}) };\n}\n","/**\n * The render graph of a static ad drawn as a native surface (`native-surface.ts`), rebuilt from\n * prompt.json on every run:\n *\n * photo_<n> (image_generate: one plain photo the screen holds, no words) role native_photo\n * ref_photo_<n> (ingest: a photo of ours the spec names by `image`) role native_photo\n * native_<size> (static_native: the screen drawn at that size) role static_native\n * final (collect, one label per format)\n *\n * The prompt, the reference, the logo and font ingests of the canvas are kept; everything the\n * image route had (variants, prefilter, master, re-lays) goes, since no picture of the ad is\n * painted. Whether a static ad takes this route is prompt.json's to say (`native`), so adding\n * the block to an existing ad switches it on the next run, and removing it is a re-scaffold.\n *\n * Pure.\n */\nimport { nearestSupportedAspectRatio } from \"@baker/canvas-contract\";\nimport type { NodeDecl } from \"../schema/canvas.ts\";\nimport { avatarPhotoIds, type InterfaceWords, type NativePhoto, nativePhotosShown } from \"./native-surface.ts\";\nimport type { AdSpec } from \"./static-ad-spec.ts\";\nimport { BRAND_LOGO_ROLE, STATIC_AD_ROLE } from \"./static-render-briefs.ts\";\nimport { formatSlug } from \"./static-topology.ts\";\n\n/** A photo a native screen holds, rendered or ours. */\nexport const NATIVE_PHOTO_ROLE = \"native_photo\";\n/** One delivered size of a native screen. */\nexport const STATIC_NATIVE_ROLE = \"static_native\";\n/** The brand's square mark, an avatar on a native screen. */\nexport const BRAND_MARK_ROLE = \"brand_mark\";\n\ntype Node = {\n id?: unknown;\n type?: unknown;\n params?: Record<string, unknown>;\n inputs?: Record<string, unknown>;\n tags?: { role?: unknown };\n};\n\nfunction nodesOf(canvas: unknown): Node[] {\n const nodes = (canvas as { nodes?: unknown } | null)?.nodes;\n return Array.isArray(nodes) ? (nodes as Node[]) : [];\n}\n\n/** Whether the canvas draws its ad as a native surface. */\nexport function isNativeCanvas(canvas: unknown): boolean {\n return nodesOf(canvas).some((n) => n.tags?.role === STATIC_NATIVE_ROLE);\n}\n\n/** Where the people in a photo are from, and that they are ordinary people of there. */\nfunction peopleOf(spec: Pick<AdSpec, \"viewer\">): string {\n const country = spec.viewer?.market?.country;\n const where = country && country.toLowerCase() !== \"global\" ? ` who lives in ${countryName(country)}` : \"\";\n return `Anyone in it is an ordinary person${where}, the age and look the description gives, never a model: real skin with its lines and pores, everyday clothes, an unposed moment, unretouched.`;\n}\n\nconst COUNTRIES: Record<string, string> = {\n ES: \"Spain\",\n MX: \"Mexico\",\n AR: \"Argentina\",\n CO: \"Colombia\",\n CL: \"Chile\",\n PE: \"Peru\",\n BR: \"Brazil\",\n PT: \"Portugal\",\n US: \"the United States\",\n GB: \"the United Kingdom\",\n UK: \"the United Kingdom\",\n FR: \"France\",\n DE: \"Germany\",\n IT: \"Italy\",\n};\n\nfunction countryName(code: string): string {\n return COUNTRIES[code.toUpperCase()] ?? code;\n}\n\n/**\n * The prompt of one photo the screen holds: a plain photo, as a person would post it or a chat\n * would show it, with no words in it (the screen prints every word). Tier 1, Oct 2026: the people\n * the image model drew for an ad were \"young, good looking, very AI\" for a trade course aimed at\n * 45-60 year olds in Spain; so who is in it comes from the description and the market, and how\n * they look is a phone photo of ordinary people. Pure.\n */\nexport function nativePhotoBrief(photo: NativePhoto, spec: Pick<AdSpec, \"viewer\">, use: \"avatar\" | \"picture\"): string {\n const kind =\n photo.look === \"illustration\"\n ? \"An illustration\"\n : use === \"avatar\"\n ? \"A casual profile photo a person would use on their own account, head and shoulders, taken on a phone\"\n : \"A photo taken on a phone, the way a real person would post it\";\n return [\n `${kind}: ${photo.what.trim().replace(/\\.$/, \"\")}.`,\n photo.look === \"photo\" ? peopleOf(spec) : \"\",\n photo.look === \"photo\" ? \"Natural light, true colours, a little imperfect, nothing staged or stock-like.\" : \"\",\n \"No text, letters, numbers, logos, watermarks, borders, frames or interface anywhere in the picture.\",\n ]\n .filter(Boolean)\n .join(\" \");\n}\n\n/** The ratio `model` is asked for, for a photo's shape on the screen: the nearest it takes (the screen crops to the shape). */\nfunction photoRatio(photo: NativePhoto, use: \"avatar\" | \"picture\", model: string): string {\n return use === \"avatar\" ? \"1:1\" : nearestSupportedAspectRatio(\"image_generate\", model, photo.ratio);\n}\n\n/** Whether a photo is somebody's avatar on the screen (else a picture in a post or a card). */\nfunction useOf(photo: NativePhoto, spec: Pick<AdSpec, \"native\">): \"avatar\" | \"picture\" {\n return spec.native && avatarPhotoIds(spec.native).has(photo.id) ? \"avatar\" : \"picture\";\n}\n\nexport type NativeGraphOpts = {\n /** The image model the photos render on, and what each request asks for besides the prompt. */\n genModel: string;\n genParams: Record<string, unknown>;\n /** Every delivered format, in delivery order. */\n formats: readonly string[];\n /** The ad set's placements the 9:16 keeps clear of. */\n placements?: readonly string[];\n /** Paths (or URLs) of every variant of the brand's logo, and its square mark. */\n logos: readonly string[];\n mark?: string;\n /** The `$ref` of the brand's headline face, when the canvas ingests one. */\n fontRef?: string;\n /** Where a photo of ours (`photos[].image`) resolves from: the creative's folder. */\n resolve: (image: string) => { source: \"path\"; path: string } | { source: \"url\"; url: string };\n /** The interface's words written for the viewer's language (`needsInterfaceWords`), drawn by every screen. */\n interfaceWords?: InterfaceWords;\n};\n\n/** The ids the graph's own ingests take. */\nfunction logoId(i: number): string {\n return i === 0 ? \"brand_logo\" : `brand_logo_${i + 1}`;\n}\n\n/**\n * The canvas drawn as the spec's native surface: its spine (the prompt, the reference, the font)\n * kept, the brand's logos and mark ingested, one photo node per photo the screen shows and one\n * screen per size. Pure: canvas and spec in, the new canvas out.\n */\nexport function nativeCanvas<C extends { nodes: unknown[] }>(canvas: C, spec: AdSpec, opts: NativeGraphOpts): C {\n if (!spec.native) return canvas;\n const kept = nodesOf(canvas).filter(\n (n) => n.tags?.role === STATIC_AD_ROLE || n.id === \"original\" || n.id === \"font_headline\",\n ) as NodeDecl[];\n const logos: NodeDecl[] = opts.logos.map((file, i) => ({\n id: logoId(i),\n type: \"ingest\",\n params: { ...locate(file, opts), expect: \"image\" },\n ...(i === 0 ? { tags: { role: BRAND_LOGO_ROLE } } : {}),\n }));\n const mark: NodeDecl[] = opts.mark\n ? [\n {\n id: \"brand_mark\",\n type: \"ingest\",\n params: { ...locate(opts.mark, opts), expect: \"image\" },\n tags: { role: BRAND_MARK_ROLE },\n },\n ]\n : [];\n const shown = nativePhotosShown(spec.native);\n const photos: NodeDecl[] = shown.map((photo) => {\n const id = photo.id.toLowerCase();\n if (photo.image) {\n return {\n id: `ref_${id}`,\n type: \"ingest\",\n params: { ...opts.resolve(photo.image), expect: \"image\" },\n tags: { role: NATIVE_PHOTO_ROLE },\n };\n }\n const use = useOf(photo, spec);\n return {\n id,\n type: \"image_generate\",\n params: {\n model: opts.genModel,\n aspect_ratio: photoRatio(photo, use, opts.genModel),\n ...opts.genParams,\n prompt: nativePhotoBrief(photo, spec, use),\n },\n tags: { role: NATIVE_PHOTO_ROLE },\n };\n });\n const photoRefs = shown.map((p) =>\n p.image ? `$ref:ref_${p.id.toLowerCase()}.asset` : `$ref:${p.id.toLowerCase()}.images#0`,\n );\n const screens: NodeDecl[] = opts.formats.map((format) => ({\n id: `native_${formatSlug(format)}`,\n type: \"static_native\",\n inputs: {\n spec: \"$ref:prompt.asset\",\n ...(logos.length > 0 ? { logos: logos.map((l) => `$ref:${l.id}.asset`) } : {}),\n ...(mark.length > 0 ? { mark: \"$ref:brand_mark.asset\" } : {}),\n ...(opts.fontRef ? { font: opts.fontRef } : {}),\n ...(photoRefs.length > 0 ? { photos: photoRefs } : {}),\n },\n params: {\n format,\n ...(opts.placements ? { placements: [...opts.placements] } : {}),\n photo_ids: shown.map((p) => p.id),\n ...(opts.interfaceWords ? { interface_words: opts.interfaceWords } : {}),\n },\n tags: { role: STATIC_NATIVE_ROLE },\n }));\n const final: NodeDecl = {\n id: \"final\",\n type: \"collect\",\n inputs: { images: screens.map((s) => `$ref:${s.id}.image`) },\n params: { labels: [...opts.formats] },\n };\n return { ...canvas, nodes: [...kept, ...logos, ...mark, ...photos, ...screens, final] };\n}\n\nfunction locate(file: string, opts: NativeGraphOpts) {\n return /^https?:\\/\\//i.test(file) ? { source: \"url\" as const, url: file } : opts.resolve(file);\n}\n\n/** The formats and placements a static canvas delivers, and the image model it renders on, read back off it. */\nexport function deliveryOf(canvas: unknown): {\n formats: string[];\n placements?: string[];\n genModel?: string;\n genParams: Record<string, unknown>;\n} {\n const nodes = nodesOf(canvas);\n const labels = nodes.find((n) => n.id === \"final\" && n.type === \"collect\")?.params?.labels;\n const formats = Array.isArray(labels) ? labels.filter((l): l is string => typeof l === \"string\") : [];\n const withPlacements = nodes.find((n) => Array.isArray(n.params?.placements));\n const placements = withPlacements?.params?.placements as string[] | undefined;\n const render = nodes.find(\n (n) =>\n n.type === \"image_generate\" &&\n (n.tags?.role === NATIVE_PHOTO_ROLE ||\n n.tags?.role === \"static_variants\" ||\n n.tags?.role === \"static_variant_call\"),\n );\n const genParams = { ...(render?.params ?? {}) };\n for (const key of [\"model\", \"prompt\", \"aspect_ratio\", \"n\"]) delete genParams[key];\n return {\n formats,\n ...(placements ? { placements } : {}),\n ...(typeof render?.params?.model === \"string\" ? { genModel: render.params.model } : {}),\n genParams,\n };\n}\n","import { readFile, stat, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { BRAND_DIR } from \"../../engine/scaffold/ad-brand-source.ts\";\nimport { AdSpecSchema, isAdSpec, readAdSpec, referenceNodeId } from \"../../engine/scaffold/static-ad-spec.ts\";\nimport { addNamedPeople, type FoundAvatar, type PeopleReader } from \"../../engine/scaffold/static-named-people.ts\";\nimport { type CitationJudge, citedPlatforms } from \"../../engine/scaffold/review-platforms.ts\";\nimport { addProofBadges } from \"../../engine/scaffold/static-proof.ts\";\nimport { readStaticBrandKit } from \"../../engine/scaffold/static-brand.ts\";\nimport { projectAdaptBriefs } from \"../../engine/scaffold/static-adapt-briefs.ts\";\nimport {\n BRAND_LOGO_ROLE,\n isStaticBriefCanvas,\n projectStaticBriefs,\n} from \"../../engine/scaffold/static-render-briefs.ts\";\nimport { LOGO_COMPOSITE_ROLE } from \"../../engine/scaffold/static-topology.ts\";\n\n/**\n * Keep a static ad's render prompts where the agent edits the ad: `prompt.json`.\n *\n * The agent writes the ad spec in prompt.json (the canvas is not for hand edits), and every\n * render prompt is compiled from it (`static-render-briefs.ts`). Frozen at scaffold time, the\n * render was told the SOURCE ad's words while prompt.json said ours. This compiles the briefs\n * again before anything bills, the way `syncSceneNodeParams` re-flows scene files;\n * `prepareCanvas` then writes the result back to the canvas file, so the file (and the\n * dashboard) shows the prompt that renders, and an edit re-keys exactly the nodes it touches.\n * A canvas scaffolded before the compiler is upgraded in place the same way.\n */\ntype StaticCopySyncResult = \"regenerated\" | \"up_to_date\" | \"not_applicable\";\n\ntype CanvasNode = {\n id?: unknown;\n type?: unknown;\n params?: Record<string, unknown>;\n inputs?: Record<string, unknown>;\n tags?: { role?: unknown };\n};\n\nfunction nodesOf(canvas: unknown): CanvasNode[] {\n const nodes = (canvas as { nodes?: unknown } | null)?.nodes;\n return Array.isArray(nodes) ? (nodes as CanvasNode[]) : [];\n}\n\n/** The path of the `prompt` ingest a static ad renders from, once paths are resolved. */\nfunction blueprintPath(nodes: CanvasNode[]): string | null {\n const prompt = nodes.find((n) => n.id === \"prompt\" && n.type === \"ingest\");\n const p = prompt?.params;\n if (p?.source !== \"path\" || p.expect !== \"json\" || typeof p.path !== \"string\" || p.path.includes(\"[TODO\")) {\n return null;\n }\n return p.path;\n}\n\n/** The content of the prompt.json a static canvas renders from, or null when it has none. Throws on unreadable JSON. */\nexport async function readStaticPromptJson(canvas: unknown): Promise<unknown | null> {\n const source = blueprintPath(nodesOf(canvas));\n return source ? JSON.parse(await readFile(source, \"utf8\")) : null;\n}\n\n/**\n * The workspace whose brand a folder draws with: the nearest folder at or above it that holds\n * `src/brand`. Not `findWorkspaceRoot` (normalize-paths.ts), which stops at the nearest\n * `package.json` and anchors path healing and `canvas/` outputs; this one only feeds the brand kit.\n */\nexport async function findBrandRoot(start: string): Promise<string | null> {\n let dir = path.resolve(start);\n for (;;) {\n if (\n await stat(path.join(dir, BRAND_DIR)).then(\n (s) => s.isDirectory(),\n () => false,\n )\n )\n return dir;\n const parent = path.dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n}\n\n/**\n * Compile every render prompt (the takes, the re-lays, the judge) and the type specimen's\n * text from prompt.json, in place. Call AFTER `resolveRelativeCanvasPaths` and\n * `syncStaticAdLogo`. Throws when prompt.json is unreadable, not JSON, or an invalid ad spec:\n * the caller must stop before billing.\n */\nexport async function syncStaticAdBrief(canvas: unknown, log: (line: string) => void): Promise<StaticCopySyncResult> {\n if (!isStaticBriefCanvas(canvas)) return \"not_applicable\";\n const promptJson = await readStaticPromptJson(canvas);\n if (promptJson === null) return \"not_applicable\";\n const changed = projectStaticBriefs(canvas, promptJson) + projectAdaptBriefs(canvas, promptJson);\n if (changed === 0) return \"up_to_date\";\n log(\"[brief] the ad was re-read from prompt.json; each render's brief and the type specimen now show it\");\n return \"regenerated\";\n}\n\n/**\n * The canvas without the composites that place the logos `ids` on its sizes: whatever read a\n * composite reads the size under it.\n */\nfunction dropLogoComposites<N extends { id?: unknown; inputs?: Record<string, unknown>; tags?: { role?: unknown } }>(\n nodes: N[],\n ids: ReadonlySet<string>,\n): N[] {\n const under = new Map<string, string>();\n for (const n of nodes) {\n const logo = String(n.inputs?.logo ?? \"\").match(/^\\$ref:([a-z][a-z0-9_]*)\\./)?.[1];\n if (n.tags?.role === LOGO_COMPOSITE_ROLE && logo && ids.has(logo) && typeof n.inputs?.image === \"string\") {\n under.set(`$ref:${String(n.id)}.image`, n.inputs.image);\n }\n }\n if (under.size === 0) return nodes;\n const dropped = new Set([...under.keys()].map((ref) => ref.slice(\"$ref:\".length, -\".image\".length)));\n return nodes\n .filter((n) => !dropped.has(String(n.id)))\n .map((n) => {\n if (!n.inputs) return n;\n const text = JSON.stringify(n.inputs);\n const next = text.replace(/\"(\\$ref:[a-z][a-z0-9_]*\\.image)\"/g, (m, ref: string) =>\n under.has(ref) ? JSON.stringify(under.get(ref)) : m,\n );\n return next === text ? n : { ...n, inputs: JSON.parse(next) as Record<string, unknown> };\n });\n}\n\n/**\n * Keep the logo where the agent decides it: `prompt.json`. The scaffold wires the brand's\n * logo file when the reference shows one; `\"logo\": false` in the ad spec (or, in an older\n * blueprint, deleting the advertiser entry from `brands_logos`) is how the concept drops it,\n * and that has to reach the canvas on every run, or the model is still handed the file while\n * the review expects none. Removes the logo ingest and its reference in every render; the\n * brief compiled after it then asks for no logo. The reverse (a logo added to a canvas\n * scaffolded without the file) needs the file wired, so it is only reported. Throws when\n * prompt.json is unreadable, like the brief sync.\n */\n/**\n * The brand's logo file wired into a static canvas: one `brand_logo` ingest before the first\n * render, and its picture added to every render's references (the variants and each re-laid-out\n * size), so the brief projection that follows numbers it and tells the model to reproduce it.\n */\nfunction wireLogo(holder: { nodes?: unknown }, nodes: CanvasNode[], renders: CanvasNode[], file: string): void {\n const ref = \"$ref:brand_logo.asset\";\n for (const node of renders) {\n const reference = node.inputs?.reference;\n const list = Array.isArray(reference) ? reference : reference === undefined ? [] : [reference];\n node.inputs = { ...node.inputs, reference: [...list, ref] };\n }\n const logo: CanvasNode = {\n id: \"brand_logo\",\n type: \"ingest\",\n params: { source: \"path\", path: file, expect: \"image\" },\n tags: { role: BRAND_LOGO_ROLE },\n };\n const at = nodes.findIndex((n) => renders.includes(n));\n holder.nodes = [...nodes.slice(0, at), logo, ...nodes.slice(at)];\n}\n\nexport async function syncStaticAdLogo(canvas: unknown, log: (line: string) => void): Promise<StaticCopySyncResult> {\n const holder = canvas as { nodes?: unknown };\n const nodes = nodesOf(canvas);\n const source = blueprintPath(nodes);\n const logoNodes = nodes.filter((n) => n.type === \"ingest\" && n.tags?.role === BRAND_LOGO_ROLE);\n const renders = nodes.filter((n) => n.type === \"image_generate\" && typeof n.params?.prompt === \"string\");\n if (!source || renders.length === 0 || !isStaticBriefCanvas(canvas)) return \"not_applicable\";\n const wanted = readAdSpec(JSON.parse(await readFile(source, \"utf8\"))).logo;\n if (wanted && logoNodes.length === 0) {\n const root = await findBrandRoot(path.dirname(source));\n const file = root ? (await readStaticBrandKit(root)).logo : undefined;\n if (!file) {\n log(\"[logo] prompt.json keeps the logo but src/brand/logos/ has no real logo file: add it and the next run wires it\");\n return \"up_to_date\";\n }\n wireLogo(holder, nodes, renders, file);\n log(`[logo] prompt.json keeps the logo: wired the brand's logo file (${path.basename(file)}) into every render`);\n return \"regenerated\";\n }\n if (!wanted && logoNodes.length > 0) {\n const ids = new Set(logoNodes.map((n) => String(n.id)));\n for (const node of renders) {\n const inputs = node.inputs ?? {};\n const reference = inputs.reference;\n if (Array.isArray(reference)) {\n const kept = reference.filter((r) => !ids.has(String(r).match(/^\\$ref:([a-z][a-z0-9_]*)\\./)?.[1] ?? \"\"));\n if (kept.length > 0) inputs.reference = kept;\n else delete inputs.reference;\n }\n }\n holder.nodes = dropLogoComposites(nodes, ids).filter((n) => !ids.has(String(n.id)));\n log(\"[logo] prompt.json no longer keeps the logo, so the logo file is unwired and the render is told to draw none\");\n return \"regenerated\";\n }\n return \"up_to_date\";\n}\n\nconst REF_NODE = /^ref_[a-z]+_\\d+$/;\n\n/**\n * Wire the pictures prompt.json names (`references[].image`) into a static canvas: one ingest per\n * picture (`ref_<id>`, a path from the creative's folder or a URL), added to every render's\n * references so the brief numbers it; a picture prompt.json no longer names is unwired. So a\n * picture of ours is named where the ad is written, never hand-wired in canvas.json (audited\n * Sept 2026: 64 hand edits of canvas.json in 8 chats). Runs before the brief projection.\n */\nexport async function syncStaticAdReferences(canvas: unknown, log: (line: string) => void): Promise<StaticCopySyncResult> {\n const holder = canvas as { nodes?: unknown };\n const nodes = nodesOf(canvas);\n const source = blueprintPath(nodes);\n const renders = nodes.filter((n) => n.type === \"image_generate\" && typeof n.params?.prompt === \"string\");\n if (!source || renders.length === 0 || !isStaticBriefCanvas(canvas)) return \"not_applicable\";\n const spec = readAdSpec(JSON.parse(await readFile(source, \"utf8\")));\n const wanted = new Map(\n spec.references.flatMap((r) => (r.image ? [[referenceNodeId(r), { id: r.id, image: r.image }] as const] : [])),\n );\n const paramsOf = (image: string) =>\n /^https?:\\/\\//i.test(image)\n ? { source: \"url\", url: image, expect: \"image\" }\n : { source: \"path\", path: path.resolve(path.dirname(source), image), expect: \"image\" };\n let changed = false;\n const stale = new Set(\n nodes.filter((n) => typeof n.id === \"string\" && REF_NODE.test(n.id) && !wanted.has(n.id)).map((n) => String(n.id)),\n );\n let next = nodes.filter((n) => !stale.has(String(n.id)));\n if (stale.size > 0) changed = true;\n for (const render of renders) {\n const reference = render.inputs?.reference;\n if (!Array.isArray(reference)) continue;\n const kept = reference.filter((r) => !stale.has(String(r).match(/^\\$ref:([a-z][a-z0-9_]*)\\./)?.[1] ?? \"\"));\n if (kept.length === reference.length) continue;\n if (kept.length > 0) render.inputs = { ...render.inputs, reference: kept };\n else {\n const { reference: _r, ...rest } = render.inputs ?? {};\n render.inputs = rest;\n }\n }\n for (const [id, { id: label, image }] of wanted) {\n const params = paramsOf(image);\n const existing = next.find((n) => n.id === id);\n if (existing) {\n if (JSON.stringify(existing.params) !== JSON.stringify(params)) {\n existing.params = params;\n changed = true;\n }\n continue;\n }\n const at = next.findIndex((n) => renders.includes(n));\n next = [...next.slice(0, at), { id, type: \"ingest\", params }, ...next.slice(at)];\n for (const render of renders) {\n const reference = render.inputs?.reference;\n const list = Array.isArray(reference) ? reference : reference === undefined ? [] : [reference];\n render.inputs = { ...render.inputs, reference: [...list, `$ref:${id}.asset`] };\n }\n log(`[pictures] prompt.json shows ${label} from ${image}: wired into every render`);\n changed = true;\n }\n holder.nodes = next;\n return changed ? \"regenerated\" : \"up_to_date\";\n}\n\n/**\n * Wire the real badge of every review platform prompt.json's copy cites (D3), writing it into\n * prompt.json where the ad is written: the platform's logo (`fetchLogo`, `baker images logo`) as\n * a `BADGE_n` reference and the platform as a proof brand. Run before the render's gate, so a\n * claim gets its source instead of a refusal; a claim the agent deletes later leaves a badge it\n * can delete too. Returns whether prompt.json changed. Never throws on a lookup that fails.\n */\nexport async function wireProofBadges(\n promptPath: string,\n fetchLogo: (domain: string) => Promise<string | null>,\n log: (line: string) => void,\n /** Which platform each line cites for reviews (`citedPlatforms`); the decision model unless given. */\n judge?: CitationJudge,\n): Promise<boolean> {\n let raw: unknown;\n try {\n raw = JSON.parse(await readFile(promptPath, \"utf8\"));\n } catch {\n return false;\n }\n if (!isAdSpec(raw)) return false;\n // A prompt.json with a bad field is the preflight's to report, in its usual words.\n const read = AdSpecSchema.safeParse(raw);\n if (!read.success) return false;\n const spec = read.data;\n const wired = await addProofBadges(spec, fetchLogo, await citedPlatforms(spec.copy.map((l) => l.text), judge));\n if (wired === spec) return false;\n const added = wired.third_party_brands.filter((b) => !spec.third_party_brands.some((o) => o.name === b.name));\n const next = { ...(raw as Record<string, unknown>), references: wired.references, third_party_brands: wired.third_party_brands };\n await writeFile(promptPath, `${JSON.stringify(next, null, 2)}\\n`, \"utf8\");\n log(`[proof] the copy cites ${added.map((b) => b.name).join(\", \")}: wired its real badge into prompt.json (\\`references\\`, \\`third_party_brands\\`)`);\n return true;\n}\n\n/**\n * Cast every real person prompt.json's copy names as themselves (a quote signed \"Lautaro Suarez,\n * CTO at Vibiz\", the founder): `research` finds their avatar (the company's, else built from their\n * public photos) and its identity sheet is written into prompt.json as a `PERSON_n` reference, so\n * every render shows their real face, never a stranger wearing their name (Oct 2026 bench). A\n * person it cannot find is left to the ad as written. Returns whether prompt.json changed.\n */\nexport async function wireNamedPeople(\n promptPath: string,\n research: (who: string) => Promise<FoundAvatar | null>,\n log: (line: string) => void,\n /** Who the copy names (`peopleReader`); with no reading, nobody is cast. */\n readNames: PeopleReader,\n): Promise<boolean> {\n let raw: unknown;\n try {\n raw = JSON.parse(await readFile(promptPath, \"utf8\"));\n } catch {\n return false;\n }\n if (!isAdSpec(raw)) return false;\n const read = AdSpecSchema.safeParse(raw);\n if (!read.success || read.data.native) return false;\n const spec = read.data;\n const people = (await readNames(spec.copy.map((l) => l.text)).catch(() => null)) ?? [];\n if (people.length === 0) return false;\n const cast = await addNamedPeople(spec, research, people);\n if (cast === spec) return false;\n const added = cast.references.filter((r) => !spec.references.some((o) => o.id === r.id));\n await writeFile(promptPath, `${JSON.stringify({ ...(raw as Record<string, unknown>), references: cast.references }, null, 2)}\\n`, \"utf8\");\n for (const ref of added) {\n log(\n `[people] the copy names a real person: cast them as themselves (@${ref.avatar}, ${ref.id} in prompt.json \\`references\\`). If \\`scene\\` or \\`direction\\` says there is no person, say where their photo sits instead.`,\n );\n }\n return true;\n}\n","/**\n * Real people a static ad's copy names: a quote signed \"Lautaro Suarez, CTO at Vibiz\", the founder,\n * a named team member. They are shown as themselves, from their own public photos (`baker avatars\n * research`), never as an invented face wearing their name (Oct 2026 bench: a CTO's quote drawn\n * beside a stranger). Who a line names is read by a text model, in any language or script\n * (`peoplePrompt`, `readPeople`); no list of job titles or capital letters decides it. Pure.\n */\nimport { z } from \"zod\";\nimport type { AdReference, AdSpec } from \"./static-ad-spec.ts\";\n\nexport type NamedPerson = {\n /** The copy line that names them. */\n line: string;\n name: string;\n /** How the research is asked for them: their name and the affiliation the line gives. */\n who: string;\n};\n\n/** Bump when the prompt changes who counts: cached readings of the old prompt are not reused. */\nexport const PEOPLE_PROMPT_VERSION = 1;\n\n/** What the model is asked about the copy: every real person a line names with who they are. Pure. */\nexport function peoplePrompt(lines: readonly string[]): string {\n return [\n 'Below are the lines of copy of an ad, numbered, in any language or several. List every real, specific person a line names together with who they are: a full name with their role or their company, as a quote\\'s signature or a team member is written (\"Lautaro Suarez, CTO at Vibiz\", \"— María López, Directora de Marketing en Acme\").',\n 'Not a person: a first name with only an initial (\"Enrique G., alumno\"), a name with no role and no company, a place, a product, a brand or a list of services.',\n \"\",\n \"For each person give:\",\n '- \"line\": the number of the line that names them',\n '- \"name\": their name exactly as the line writes it',\n '- \"affiliation\": their role, company or both, as the line gives them',\n \"\",\n \"Lines:\",\n ...lines.map((line, i) => `${i + 1}. ${JSON.stringify(line)}`),\n \"\",\n 'Reply with only this JSON: {\"people\": [{\"line\": 1, \"name\": \"…\", \"affiliation\": \"…\"}]}',\n ].join(\"\\n\");\n}\n\nconst Person = z.object({\n line: z.number().int(),\n name: z.string().trim().min(1),\n affiliation: z.string().trim().min(1),\n});\nconst Answer = z.object({ people: z.array(z.unknown()) });\n\nconst folded = (text: string) =>\n text\n .normalize(\"NFD\")\n .replace(/\\p{Diacritic}/gu, \"\")\n .toLowerCase();\n\n/**\n * The people a model's answer about `lines` names, each kept only when its line really writes the\n * name (a model never casts a name the copy does not hold; the affiliation may come back reworded,\n * \"cofundadora\" of \"cofundadores\"); null when the answer is not that shape at all. Pure.\n */\nexport function readPeople(answer: unknown, lines: readonly string[]): NamedPerson[] | null {\n const parsed = Answer.safeParse(answer);\n if (!parsed.success) return null;\n return parsed.data.people.flatMap((raw) => {\n const person = Person.safeParse(raw);\n if (!person.success) return [];\n const { line: n, name, affiliation } = person.data;\n const line = lines[n - 1];\n if (!line || !folded(line).includes(folded(name))) return [];\n return [{ line, name, who: `${name}, ${affiliation}` }];\n });\n}\n\n/** The people `lines` name (`readPeople`), or null when no model could read them. */\nexport type PeopleReader = (lines: readonly string[]) => Promise<NamedPerson[] | null>;\n\n/** A named person's avatar, as the research answers it: the handle and the identity sheet to ground on. */\nexport type FoundAvatar = { handle: string; sheetUrl: string };\n\nconst isCast = (references: readonly AdReference[], person: NamedPerson) =>\n references.some((r) => r.id.startsWith(\"PERSON_\") && r.avatar && folded(r.what).includes(folded(person.name)));\n\n/** The people the copy names (`people`, `readPeople`) that no person reference casts: the review blocks any face shown as them. */\nexport function uncastPeople(spec: Pick<AdSpec, \"references\">, people: readonly NamedPerson[]): string[] {\n return people.filter((person) => !isCast(spec.references, person)).map((person) => person.name);\n}\n\n/**\n * The spec with every person its copy names (`people`, `readPeople`) cast as themselves: `research(who)` finds their avatar\n * (the one the company has, else built from their public photos), and its identity sheet becomes a\n * `PERSON_n` reference, so every render shows their real face. A person already cast (a person\n * reference with an avatar that names them) is not asked about; one the research cannot find adds\n * nothing. Returns `spec` itself when nothing changed. Never throws on a lookup that fails.\n */\nexport async function addNamedPeople(\n spec: AdSpec,\n research: (who: string) => Promise<FoundAvatar | null>,\n people: readonly NamedPerson[],\n): Promise<AdSpec> {\n const references: AdReference[] = [...spec.references];\n for (const person of people) {\n if (isCast(references, person)) continue;\n const found = await research(person.who).catch(() => null);\n if (!found) continue;\n references.push({\n id: `PERSON_${nextPersonNumber(references)}`,\n role: \"identity\",\n what: `${person.name}, the real person \"${person.line}\" names, shown as themselves with the face in this picture: as the scene places them, else a small round profile photo beside that line`,\n avatar: found.handle,\n image: found.sheetUrl,\n });\n }\n return references.length === spec.references.length ? spec : { ...spec, references };\n}\n\n/** One past the highest `PERSON_n` already used, so a new id never repeats one (ids can have gaps). */\nfunction nextPersonNumber(references: readonly AdReference[]): number {\n const used = references.flatMap((r) => {\n const n = /^PERSON_(\\d+)$/.exec(r.id)?.[1];\n return n ? [Number(n)] : [];\n });\n return Math.max(0, ...used) + 1;\n}\n","/**\n * Proof on a static ad: a line that cites a review platform carries that platform's real badge\n * (D3), and a customer's words carry their name. The scaffold wires the badges (`addProofBadges`);\n * every run refuses an ad whose proof has none (`badgeProblems`, `unattributedQuotes`) before\n * anything bills. Whether a figure or a superlative is one the records hold is `claim-check.ts`.\n *\n * Pure, but for the logo lookup `addProofBadges` is handed and the decision model `readSignatures` asks.\n */\nimport { askJev } from \"../nodes/local/lib/jev.ts\";\nimport type { ReviewUsage } from \"../nodes/local/lib/render-review.ts\";\nimport { type Citations, citedBy } from \"./review-platforms.ts\";\nimport type { AdReference, AdSpec, AdThirdPartyBrand } from \"./static-ad-spec.ts\";\n\n/** The platform's badge is wired: a third-party brand of that name whose logo reference has a picture. */\nfunction badgeWired(spec: AdSpec, name: string): boolean {\n const brand = spec.third_party_brands.find((b) => b.name.toLowerCase() === name.toLowerCase());\n if (!brand?.logo) return false;\n const ref = spec.references.find((r) => r.id === brand.logo);\n return Boolean(ref && (ref.image || ref.slot));\n}\n\n/** This many words of a customer's quote in a row, in one copy line, set their words as the ad's. */\nconst QUOTED_RUN = 5;\n/** A line that signs a quote: its role, or a dash before a name (\"— Lena Fischer\"). */\nconst SIGNING_ROLE = /^(attribution|author|byline|name|reviewer|source|signature)$/i;\n/**\n * A signature by its shape alone, in any script and either case: a long dash opening the line, or a\n * dash right after the quote that opens the line closes, before a short close (\"«…» - lucía,\n * Valencia\"). A dash inside a sentence (\"Rápido — y gratis\") signs nothing, nor a minus before a\n * figure (\"-30% en tu primera factura\").\n */\nconst LEADING_DASH = /^\\s*[—–―]\\s*\\p{L}/u;\nconst QUOTED_THEN_DASH =\n /^\\s*[\\p{Pi}\"„「『][^\\p{Pf}\"」』]+[\\p{Pf}\"」』]\\s*(?:[—–―]\\s*|-\\s+)\\p{L}[^—–―\\p{Pi}\\p{Pf}\"]{0,40}$/u;\nconst NAME_WORD = \"\\\\p{Lu}[\\\\p{L}\\\\p{M}'’-]*\";\n/** A spaced dash closing the line with a first name and an initial (\"…no mess - Lena F.\", \"— Mary Ann K., Leeds\"). */\nconst SIGNED_WITH_INITIAL = new RegExp(\n `\\\\s[—–―-]\\\\s+${NAME_WORD}(?:\\\\s+${NAME_WORD})?\\\\s+\\\\p{Lu}\\\\.(?:,[^,—–―\\\\d]{1,40})?\\\\s*$`,\n \"u\",\n);\nconst signed = (text: string): boolean =>\n LEADING_DASH.test(text) || QUOTED_THEN_DASH.test(text) || SIGNED_WITH_INITIAL.test(text);\n/**\n * A spaced dash before a short capitalised close may sign a quote (\"…no mess - Lena Fischer, Leeds\")\n * or be the advertiser's call to action (\"Solar in one day - Free Survey\"), and so may a hyphen opening\n * a line (\"- Lena Fischer\", \"- Free installation\"), a long dash after a sentence ends (\"…happened. —\n * Lena Fischer\", \"…でした。— 田中\", \"Instalamos en un día. — Pide tu estudio\") and a dash after a\n * bracket or a quoted word (\"Paneles “premium” — instalación gratis\"): only what it means tells them apart.\n */\nconst MAYBE_SIGNED_AT_END =\n /\\s[—–―-]\\s+\\p{Lu}[\\p{L}\\p{M}.'’-]*(?:(?:\\s+[\\p{L}\\p{M}.'’-]+){0,2}\\s+\\p{Lu}[\\p{L}\\p{M}.'’-]*)?(?:,[^,—–―\\d]{1,40})?\\s*$/u;\nconst MAYBE_SIGNED_OPENING = /^\\s*-\\s+\\p{L}/u;\nconst MAYBE_SIGNED_AFTER_STOP = /[.!?…。!?]\\s*[—–―]\\s*\\p{L}|[\\p{Pf}\\p{Pe}\"]\\s*(?:[—–―]\\s*|-\\s+)\\p{L}/u;\n\n/** A line whose dash may sign a quote or be the advertiser's own copy: its meaning decides. Pure. */\nexport function maybeSigned(text: string): boolean {\n return (\n !signed(text) &&\n [MAYBE_SIGNED_AT_END, MAYBE_SIGNED_OPENING, MAYBE_SIGNED_AFTER_STOP].some((shape) => shape.test(text))\n );\n}\n\n/**\n * For refusing an unsigned quote, a quoted line's closing dash is unclear too: the close may be a\n * name (\"«…» — Lucía, Valencia\") or the advertiser's own words (\"“…” — Premium panels included\").\n * A translation still keeps such a line whole (`isCustomersWords`): the quote in it is theirs.\n */\nconst signedForSure = (text: string): boolean => LEADING_DASH.test(text) || SIGNED_WITH_INITIAL.test(text);\nconst signedUnclearly = (text: string): boolean =>\n !signedForSure(text) && (QUOTED_THEN_DASH.test(text) || maybeSigned(text));\n\n/** What a line is set as a customer's: a quote, or the name signing one. */\nconst QUOTE_ROLE = /^(quote|testimonial|review)$/i;\n\n/** A line that only signs: set as the signature, or opening with the dash before a name (\"— Lena Fischer\"). Pure. */\nexport function isSignatureLine(line: { text: string; role?: string | undefined }): boolean {\n return SIGNING_ROLE.test(line.role ?? \"\") || LEADING_DASH.test(line.text);\n}\n\n/**\n * A copy line that is a customer's own words or the name on them: signed with a dash before a\n * name, or set as a quote or its signature. A translation keeps it as they said it. Pure.\n */\nexport function isCustomersWords(line: { text: string; role?: string | undefined }): boolean {\n return signed(line.text) || QUOTE_ROLE.test(line.role ?? \"\") || SIGNING_ROLE.test(line.role ?? \"\");\n}\n\nconst quoteWords = (text: string): string[] =>\n text\n .toLowerCase()\n .split(/[^\\p{L}\\p{N}]+/u)\n .filter(Boolean);\n\nfunction quotes(line: string, quote: string): boolean {\n const said = ` ${quoteWords(line).join(\" \")} `;\n const words = quoteWords(quote);\n for (let i = 0; i + QUOTED_RUN <= words.length; i++) {\n if (said.includes(` ${words.slice(i, i + QUOTED_RUN).join(\" \")} `)) return true;\n }\n return false;\n}\n\n/** The line carries the first word of the name the research kept for the quote, beyond the quote's own words. */\nfunction namesAuthor(line: string, by: { quote: string; author?: string }): boolean {\n const first = by.author ? quoteWords(by.author)[0] : undefined;\n if (!first) return false;\n const quoted = new Set(quoteWords(by.quote));\n return !quoted.has(first) && quoteWords(line).includes(first);\n}\n\n/**\n * A customer's words set as copy with no name on them: the review blocks a quote nobody signed, and\n * a picture edit cannot invent the name (Dev, Oct 2026: three corrections in a row). The name is the\n * one the research kept (`author`); that name on the line, a dash before a name, or a line of its\n * own signs it. `signatures` holds the lines whose unclear dash (`maybeSigned`) the decision model\n * read as a name (`readSignatures`); without it, every such dash counts as one: a guess never\n * refuses an ad. Pure.\n */\nexport function unattributedQuotes(\n spec: AdSpec,\n said: ReadonlyArray<{ quote: string; author?: string }>,\n signatures?: ReadonlySet<string>,\n): string[] {\n const signedApart = spec.copy.some((line) => SIGNING_ROLE.test(line.role ?? \"\"));\n return spec.copy.flatMap((line) => {\n // The line already quotes a customer: a dash closing it with a name is their signature.\n const unclearSigned = signedUnclearly(line.text) && (!signatures || signatures.has(line.text));\n if (signedApart || signedForSure(line.text) || unclearSigned) return [];\n const by = said.find((q) => quotes(line.text, q.quote));\n if (!by || namesAuthor(line.text, by)) return [];\n const name = by.author ? `\"— ${by.author}\"` : '\"— <their first name and initial>\"';\n return [\n `\"${line.text}\" sets a customer's words with no name on them: add ${name} to the line (or a line of its own with role \"attribution\"); the review blocks a quote nobody signed.`,\n ];\n });\n}\n\n/** At or above this, the decision model reads a line's unclear dash as signing the quote with a name. */\nexport const SIGNS_QUOTE = 0.5;\n\nconst SIGNATURE_QUESTION =\n \"Does `line`, a customer's quote, carry the name of the person who said it after (or before) its dash, rather than the advertiser's own words such as a call to action or an offer, in whatever language it is written?\";\n\n/** For each line, the probability its dash signs the quote with a name; null when no judgment came back. */\nexport type SignatureJudge = (lines: string[]) => Promise<number[] | null>;\n\n/** The decision model as the judge, its spend reported to `onSpend`: one Noul per line, one request. */\nexport function jevSignatureJudge(onSpend?: (usage: ReviewUsage) => void): SignatureJudge {\n return (lines) =>\n askJev(\n lines.map((line) => ({ instructions: { line, question: SIGNATURE_QUESTION } })),\n onSpend,\n );\n}\n\n/**\n * The copy lines quoting a customer (`said`) whose unclear dash (`maybeSigned`) the decision model\n * reads as their name, for `unattributedQuotes`: one request for every such line, none when there is\n * none. Undefined when no judgment came back: an unclear dash then counts as a name.\n */\nexport async function readSignatures(\n spec: AdSpec,\n said: ReadonlyArray<{ quote: string; author?: string }>,\n judge: SignatureJudge = jevSignatureJudge(),\n): Promise<ReadonlySet<string> | undefined> {\n const asked = [\n ...new Set(\n spec.copy\n .map((line) => line.text)\n .filter((text) => signedUnclearly(text) && said.some((q) => quotes(text, q.quote) && !namesAuthor(text, q))),\n ),\n ];\n if (asked.length === 0) return new Set();\n const judged = await judge(asked).catch(() => null);\n if (judged?.length !== asked.length) return undefined;\n return new Set(asked.filter((_, k) => (judged[k] ?? 0) >= SIGNS_QUOTE));\n}\n\n/**\n * A review platform the copy names with no badge of it wired: the platform's real logo must be on\n * the ad (D3, Sept 2026: \"Reseña verificada en Trustpilot\" over generic stars). Whether the rating\n * or count it states is one the records hold is the claim gate's (`claim-check.ts`). `cited` is\n * the copy read for citations (`citedPlatforms`); without it, what each line shows alone. Pure.\n */\nexport function badgeProblems(spec: AdSpec, cited?: Citations): string[] {\n const problems: string[] = [];\n const named = new Set<string>();\n for (const line of spec.copy) {\n const platform = citedBy(line.text, cited);\n if (platform && !named.has(platform.name) && !badgeWired(spec, platform.name)) {\n named.add(platform.name);\n problems.push(\n `\"${line.text}\" names ${platform.name}, so ${platform.name}'s real badge must be on the ad: \\`baker images logo ${platform.domain}\\`, then in prompt.json a reference (\\`BADGE_1\\`, role prop, its \\`image\\` the logo's URL) and \\`third_party_brands: [{ \"name\": \"${platform.name}\", \"logo\": \"BADGE_1\", \"role\": \"proof\" }]\\`. Or drop the claim.`,\n );\n }\n }\n return problems;\n}\n\n/**\n * The spec with each review platform its copy cites wired as a proof badge: the platform's real\n * logo (`fetchLogo(domain)`, `baker images logo`) as a `BADGE_n` reference with its picture, and\n * the platform as a third-party brand whose logo that is. A platform already there, or whose\n * logo could not be fetched, is left as it is. `cited` as `badgeProblems` reads it.\n */\nexport async function addProofBadges(\n spec: AdSpec,\n fetchLogo: (domain: string) => Promise<string | null>,\n cited?: Citations,\n): Promise<AdSpec> {\n const references: AdReference[] = [...spec.references];\n const brands: AdThirdPartyBrand[] = [...spec.third_party_brands];\n let changed = false;\n const seen = new Set<string>();\n for (const line of spec.copy) {\n const platform = citedBy(line.text, cited);\n if (\n !platform ||\n seen.has(platform.name) ||\n badgeWired({ ...spec, references, third_party_brands: brands }, platform.name)\n )\n continue;\n seen.add(platform.name);\n const url = await fetchLogo(platform.domain).catch(() => null);\n if (!url) continue;\n let n = 1;\n while (references.some((r) => r.id === `BADGE_${n}`)) n++;\n const id = `BADGE_${n}`;\n references.push({ id, role: \"prop\", what: `the ${platform.name} logo`, image: url });\n const rest = brands.filter((b) => b.name.toLowerCase() !== platform.name.toLowerCase());\n brands.splice(0, brands.length, ...rest, {\n name: platform.name,\n what: \"the source of the reviews\",\n logo: id,\n role: \"proof\",\n });\n changed = true;\n }\n return changed ? { ...spec, references, third_party_brands: brands } : spec;\n}\n","import { supportedAspectRatios } from \"@baker/canvas-contract\";\n\n// Output ratios the scaffold can composite to (everything canvasDims knows).\nconst OUTPUT_ASPECTS = [\"1:1\", \"16:9\", \"9:16\", \"4:3\", \"3:4\", \"21:9\", \"4:5\", \"2:3\", \"3:2\"] as const;\n\n/** Parse \"W:H\" into a numeric ratio, or undefined when it isn't one. */\nexport function parseRatio(ar: string | undefined): number | undefined {\n const m = ar?.trim().match(/^(\\d+(?:\\.\\d+)?)[:x/](\\d+(?:\\.\\d+)?)$/);\n if (!m) return undefined;\n const w = Number(m[1]);\n const h = Number(m[2]);\n return w > 0 && h > 0 ? w / h : undefined;\n}\n\n/** The entry of `candidates` whose numeric ratio is closest to `ratio`. */\nfunction nearestAspect(ratio: number, candidates: readonly string[]): string {\n let best = candidates[0] as string;\n let bestDelta = Number.POSITIVE_INFINITY;\n for (const c of candidates) {\n const r = parseRatio(c);\n if (r === undefined) continue;\n const delta = Math.abs(ratio - r);\n if (delta < bestDelta) {\n best = c;\n bestDelta = delta;\n }\n }\n return best;\n}\n\n/**\n * Resolve the canvas's TWO aspect ratios from the deconstructed source (and an optional\n * explicit override, which wins and must be a known output ratio):\n * - `outAr` — the ad's true output ratio: drives every ffmpeg plate/still/composite dim\n * and the overlay composition size.\n * - `genAr` — the ratio clips + conditioning keyframes are GENERATED at: `outAr` when the\n * video model supports it, else the nearest supported ratio (e.g. 4:5 → 3:4; Seedance\n * has no 4:5). When they differ (`remapped`), every clip is normalized (center-cropped)\n * to `outAr` before it enters the spine — the ~6% crop is warned, never silent.\n * A source ratio like \"720:900\" snaps to the nearest known output ratio; an absent or\n * unparseable source falls back to 9:16. Pure.\n */\nexport function resolveAspect(\n sourceAr: string | undefined,\n override: string | undefined,\n genAspects: readonly string[],\n): { outAr: string; genAr: string; remapped: boolean } {\n let outAr: string;\n if (override !== undefined) {\n if (!OUTPUT_ASPECTS.includes(override as (typeof OUTPUT_ASPECTS)[number])) {\n throw new Error(`unsupported --aspect \"${override}\" — supported aspect ratios: ${OUTPUT_ASPECTS.join(\", \")}`);\n }\n outAr = override;\n } else {\n const ratio = parseRatio(sourceAr);\n outAr = ratio === undefined ? \"9:16\" : nearestAspect(ratio, OUTPUT_ASPECTS);\n }\n // `outAr` is always a known output ratio here, so it parses.\n const genAr = genAspects.includes(outAr) ? outAr : nearestAspect(parseRatio(outAr) ?? Number.NaN, genAspects);\n return { outAr, genAr, remapped: genAr !== outAr };\n}\n\n/** Canonical output pixel dims for a shared aspect ratio — social-grade, even numbers. */\nexport function canvasDims(ar: string | undefined): { w: number; h: number } {\n switch (ar) {\n case \"1:1\":\n return { w: 1080, h: 1080 };\n case \"16:9\":\n return { w: 1920, h: 1080 };\n case \"4:3\":\n return { w: 1440, h: 1080 };\n case \"3:4\":\n return { w: 1080, h: 1440 };\n case \"21:9\":\n return { w: 1920, h: 822 };\n case \"4:5\":\n return { w: 1080, h: 1350 };\n case \"2:3\":\n return { w: 1080, h: 1620 };\n case \"3:2\":\n return { w: 1620, h: 1080 };\n default:\n // 9:16 vertical — the social default and what most UGC ads ship.\n return { w: 1080, h: 1920 };\n }\n}\n\n/**\n * The aspect ratios clips are generated at on this video model: its registry\n * `aspect_ratio` enum, narrowed to the ratios the scaffold can composite (so 9:21, which\n * the keyframe image models cannot render, is never picked). Read from the registry\n * rather than matched by family: the default model (Gemini Omni) takes only 16:9 and\n * 9:16, and a family regex that fell through to Seedance's set wrote a 1:1 clip it rejects.\n * A model with no ratio enum generates at the output ratio itself.\n */\nexport function genAspectsFor(videoModel: string): readonly string[] {\n const accepted = supportedAspectRatios(\"video_generate\", videoModel);\n if (!accepted?.length) return OUTPUT_ASPECTS;\n const composable = accepted.filter((ar) => (OUTPUT_ASPECTS as readonly string[]).includes(ar));\n return composable.length > 0 ? composable : accepted;\n}\n","import {\n AD_IMAGE_MODEL,\n candidPersonPortraitPrompt,\n nearestSupportedAspectRatio,\n type VideoBlueprint,\n type VideoScene,\n} from \"@baker/canvas-contract\";\nimport { z } from \"zod\";\nimport { elementMentionKeywords } from \"../../engine/validator.ts\";\nimport type { NodeDecl } from \"../../schema/canvas.ts\";\nimport { avatarsLeftUncast, type CastAvatar, castAvatarsInOrder, recastPersonEntry } from \"../castAvatar.ts\";\nimport { ACTOR_SHEET_MODEL, assignElementLabels, elementTodo, referenceRole } from \"../lib/elements.ts\";\nimport { imageRenderParams } from \"../lib/image-params.ts\";\nimport { type Edge, ids, ref } from \"./ids.ts\";\nimport { compositeRegionsOf } from \"./routing.ts\";\n\nconst EDGES: Edge[] = [\"start\", \"end\"];\n// A video's recurring cast can change clothes (`same_as` looks), and its slots build a\n// sheet from any picture not marked as one already.\nconst VIDEO_CASTING = { looks: true, markSheet: true };\n\n// --- A recurring identity element chosen by the selection pass. ---\n// `appears_in`/`scenes` say which scene(s) — and optionally which edge — it is\n// visually present in; that drives which frames reference its shared source image.\nconst AppearsItem = z.union([z.number(), z.object({ scene: z.number(), edge: z.string().optional() }).loose()]);\nconst RecurringElement = z\n .object({\n // person | animal | product | logo | badge | other\n type: z.string(),\n label: z.string().optional(),\n description: z.string().optional(),\n expression: z.string().nullable().optional(),\n // When the element maps to a global cast entry, its stable id (for annotation).\n cast_id: z.string().nullable().optional(),\n // The label of another element that is the SAME individual as this one, shown\n // in a DIFFERENT wardrobe/persona/state (e.g. one creator playing skeptic in a\n // pink shirt and believer in a white shirt). Each look gets its own reference\n // slot, but the face/identity must stay identical across them.\n same_as: z.string().nullable().optional(),\n /**\n * A picture of this element that already exists — a cast Avatar's identity sheet,\n * a product photo, a real location shot. Given, the slot ingests it; absent, the\n * slot emits a TODO for somebody to drop one by hand.\n */\n source_image: z.string().optional(),\n /**\n * The supplied picture is ALREADY a multi-view reference sheet, so no sheet is\n * built from it.\n *\n * A cast Avatar's identity sheet is exactly that: a rendered turnaround of one\n * person, which is what `buildElementSheets` exists to produce. Fanning it into a\n * second sheet paid for an image generation to make a generation of a generation,\n * and every frame then grounded on the copy rather than on the avatar's own\n * identity — which is the drift the sheet is supposed to prevent.\n */\n source_is_sheet: z.boolean().optional(),\n // Scenes the element appears in. Either a bare list of scene indices (both\n // edges) or per-{scene,edge} entries. Both forms are accepted and merged.\n scenes: z.array(z.number()).optional(),\n appears_in: z.array(AppearsItem).optional(),\n })\n .loose();\nexport type RecurringElementT = z.infer<typeof RecurringElement>;\nexport const RecurringElements = z.array(RecurringElement);\n\n/**\n * Cast company Avatars into the recurring person slots, in order: the first distinct\n * person gets the first avatar, and so on. A `same_as` look of a cast person grounds on\n * that person's sheet too — it is the same face in other clothes.\n *\n * The sheet is the slot's picture, marked as a sheet so none is built from it. A person\n * left without an avatar keeps its TODO: casting nobody is how a canvas renders a stranger.\n */\nexport function castAvatarsOnElements(elementsInput: unknown, avatars: readonly CastAvatar[]): RecurringElementT[] {\n return castAvatarsInOrder(RecurringElements.parse(elementsInput), avatars, VIDEO_CASTING);\n}\n\n/**\n * The handles `castAvatarsOnElements` had no distinct person for, in the order given.\n * The selection keeps only recurring elements, so a one-scene presenter — or a second\n * handle on a one-person ad — leaves an avatar uncast, and that must be said, not assumed.\n */\nexport function avatarsNotCast(elementsInput: unknown, handles: readonly string[]): string[] {\n return avatarsLeftUncast(RecurringElements.parse(elementsInput), handles, VIDEO_CASTING);\n}\n\n/** Normalize an element's scene/edge presence into scene index → set of edges it appears on. */\nexport function presenceOf(el: RecurringElementT): Map<number, Set<Edge>> {\n const map = new Map<number, Set<Edge>>();\n const add = (scene: number, edge: Edge | null) => {\n if (!Number.isInteger(scene) || scene < 0) return;\n let set = map.get(scene);\n if (!set) {\n set = new Set<Edge>();\n map.set(scene, set);\n }\n if (edge) set.add(edge);\n else for (const e of EDGES) set.add(e);\n };\n for (const s of el.scenes ?? []) add(s, null);\n for (const a of el.appears_in ?? []) {\n if (typeof a === \"number\") add(a, null);\n else if (a && typeof a === \"object\") {\n const edge = a.edge === \"start\" || a.edge === \"end\" ? a.edge : null;\n add(a.scene, edge);\n }\n }\n return map;\n}\n\n/**\n * Label a cast entry with its slot, and rewrite it for the Avatar cast in the source actor's\n * place. Only the person's own slot rewrites it: a `same_as` look shares the entry, and its\n * description was written about the source actor, not the Avatar.\n */\nfunction stampCastEntry(entry: Record<string, unknown>, el: RecurringElementT, label: string): void {\n entry.reference_image = label;\n if (el.source_is_sheet && !el.same_as && el.type.toLowerCase() === \"person\" && el.description) {\n recastPersonEntry(entry, el.description);\n }\n}\n\n/**\n * Document the recurring elements in the baked prompt.json: add a top-level\n * `reference_elements` summary (label/type/description/scenes), and stamp\n * `reference_image: LABEL` onto the matching `global.cast[*]` entry by id — so\n * the human editing the JSON sees which wired slot grounds which persona. Clones\n * the input (never mutates the caller's object).\n */\nexport function annotateBlueprintWithElements(blueprintInput: unknown, elementsInput: unknown): unknown {\n if (!blueprintInput || typeof blueprintInput !== \"object\") return blueprintInput;\n const elements = RecurringElements.parse(elementsInput);\n const clone = JSON.parse(JSON.stringify(blueprintInput)) as Record<string, unknown>;\n const cast = ((clone.global as Record<string, unknown>)?.cast ?? null) as Array<Record<string, unknown>> | null;\n const summary: Array<Record<string, unknown>> = [];\n for (const { el, label } of assignElementLabels(elements, \"video\")) {\n const scenes = [...presenceOf(el).keys()].sort((a, b) => a - b);\n summary.push({ reference_image: label, type: el.type, description: el.description ?? null, scenes });\n const entry = el.cast_id && Array.isArray(cast) ? cast.find((c) => c?.id === el.cast_id) : undefined;\n if (entry && typeof entry === \"object\") stampCastEntry(entry, el, label);\n }\n clone.reference_elements = summary;\n return clone;\n}\n\n/**\n * What the generator should do with one reference slot. A `same_as` slot is the\n * SAME individual as another slot in a different wardrobe/persona — the face must\n * stay identical, only the outfit/state changes; everything else falls back to\n * the per-type role.\n */\nexport function roleForSlot(slot: ElementSlot): string {\n if (slot.sameAs) {\n const what = slot.description ? ` (${slot.description})` : \"\";\n return `the SAME individual as ${slot.sameAs}, shown in a DIFFERENT wardrobe/persona/state${what} — keep the FACE and identity IDENTICAL to the ${slot.sameAs} references; change ONLY wardrobe, styling, and expression. Ignore any caption text printed on this reference.`;\n }\n return referenceRole(slot.type, \"video\");\n}\n\nexport type ElementSlot = {\n id: string;\n ref: string;\n label: string;\n type: string;\n description?: string;\n // The label of another slot this is the same individual as (different wardrobe).\n sameAs?: string;\n // The cast id this element portrays (when the deconstruct linked it), so a speaker\n // can be matched to the element that shows them — i.e. is the presenter ON SCREEN\n // in this scene, or is it b-roll while they narrate?\n castId?: string;\n presence: Map<number, Set<Edge>>;\n // True once buildElementSheets has fused this slot's lone ingest into a multi-view\n // turnaround (`image_reference_sheet`) and repointed `ref` at the sheet, or the slot was\n // handed one (an Avatar's). Even a sheet does not stop a source frame's face leaking, so\n // a frame with people never takes the source frame (see buildFrameRef).\n sheetBacked?: boolean;\n /** The supplied picture is already a turnaround, so no sheet is built from it. */\n sourceIsSheet?: boolean;\n};\n\n/** Compute one shared ingest slot per recurring element (id, label, presence map). */\nexport function buildElementSlots(elements: RecurringElementT[]): ElementSlot[] {\n const usedIds = new Set<string>([\n ids.prompt,\n ids.spine,\n ids.overlaid,\n ids.captions,\n ids.captionsVoiceMix,\n ids.captionsTranscribe,\n ids.audioMix,\n ids.final,\n ids.musicBed,\n ]);\n const slots: ElementSlot[] = [];\n assignElementLabels(elements, \"video\").forEach(({ el, label }, i) => {\n let id = ids.element(label, i);\n while (usedIds.has(id)) id = `${id}_${i}`;\n usedIds.add(id);\n slots.push({\n id,\n ref: ref(id, \"asset\"),\n label,\n type: el.type,\n description: el.description,\n sameAs: el.same_as ?? undefined,\n castId: el.cast_id ?? undefined,\n sourceIsSheet: el.source_is_sheet === true,\n presence: presenceOf(el),\n });\n });\n return slots;\n}\n\n/** The element slots whose presence includes this scene/edge. */\nexport function slotsForFrame(slots: ElementSlot[], sceneIndex: number, edge: Edge): ElementSlot[] {\n return slots.filter((s) => s.presence.get(sceneIndex)?.has(edge));\n}\n\n// Identity-critical graphic types whose prompt mention MUST wire the reference — a\n// described-but-unwired mark renders wrong-identity (the audit's ~6\n// VIDEO_REFERENCE_MISSING logo warnings). Persons/animals are sheet-backed via cast\n// presence, and a keyword match on them would over-wire.\nconst MENTION_WIRED_TYPES = new Set([\"logo\", \"badge\"]);\n\nfunction promptMentionsSlot(prompt: string, slot: ElementSlot): boolean {\n // The SAME matcher the validator's reference-completeness check uses, so any frame\n // it would warn about is wired by construction.\n return elementMentionKeywords(slot).some((w) =>\n new RegExp(`\\\\b${w.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")}\\\\b`, \"i\").test(prompt),\n );\n}\n\n/** The scene's frame text for one edge (its own frame prompt + all region prompts). */\nfunction edgePromptText(scene: VideoScene, edge: Edge): string {\n const regionPrompts = (compositeRegionsOf(scene) ?? [])\n .map((r) => r.frame_prompt ?? \"\")\n .filter(Boolean)\n .join(\"\\n\");\n const own = (edge === \"start\" ? scene.start_frame_prompt : scene.end_frame_prompt) ?? \"\";\n return [own, regionPrompts].join(\"\\n\").trim();\n}\n\n/**\n * Extend slot presence to every scene whose frame prompts NAME the element. The\n * element-selection pass lists the scenes it noticed an element in; a frame prompt\n * can still describe it elsewhere (the deconstruct saw the logo in more scenes than\n * the selector listed), and a described-but-unwired element renders wrong-identity.\n * The prompt mention is the source of truth for wiring.\n */\nexport function extendPresenceByPromptMentions(slots: ElementSlot[], blueprint: VideoBlueprint): void {\n const extendable = slots.filter((s) => MENTION_WIRED_TYPES.has(s.type.toLowerCase()));\n if (extendable.length === 0) return;\n blueprint.scenes.forEach((scene, i) => {\n for (const edge of EDGES) {\n const text = edgePromptText(scene, edge);\n if (!text) continue;\n for (const slot of extendable) {\n if (!promptMentionsSlot(text, slot)) continue;\n const set = slot.presence.get(i) ?? new Set<Edge>();\n set.add(edge);\n slot.presence.set(i, set);\n }\n }\n });\n}\n\n// Element types that get a multi-view reference sheet, mapped to the\n// `image_reference_sheet` subject_type its schema accepts. A `location`/set is sheeted\n// too — as several CAMERA ANGLES of the same room, so a multi-scene shoot keeps one\n// consistent set (the clinic/exam-room reproduced from every angle, not a lone snapshot).\nconst SHEET_SUBJECT_TYPE: Record<string, \"person\" | \"character\" | \"product\" | \"location\"> = {\n person: \"person\",\n animal: \"character\",\n product: \"product\",\n location: \"location\",\n};\n\n/**\n * \"Build the element once, reuse everywhere.\" For each grounded base element — a\n * person, animal, product, OR location/set — fuse its dropped reference into ONE\n * multi-view sheet (`image_reference_sheet`) and repoint the slot so every frame grounds\n * on that SHEET instead of the lone ingest. The sheet is the strongest cross-scene\n * consistency lock the pipeline has: without it the same face/pet/pack/room drifts\n * clip-to-clip (a different look per scene), and a single flat photo gives the\n * generator only one angle to work from — exactly the \"we lose the essence of the\n * base asset / the angles look wrong\" failure. So we generate the multi-angle canvas\n * for EVERY recurring base element (a person/pet as a turnaround, a set as several\n * camera angles of the same room), even for a one-scene hero element (one strong\n * reference still beats a lone snapshot). Mutates `slot.ref` and sets `slot.sheetBacked`.\n * Sheets one subject per call — never a person plus their props — so each sheet stays\n * focused and clean.\n */\nexport function buildElementSheets(slots: ElementSlot[], nodes: NodeDecl[], sheetModel?: string): void {\n for (const slot of slots) {\n // Already a turnaround — an Avatar's identity sheet. Ground on it directly rather\n // than paying to render a copy of it.\n if (slot.sourceIsSheet) {\n slot.sheetBacked = true;\n continue;\n }\n const subjectType = SHEET_SUBJECT_TYPE[slot.type.toLowerCase()];\n if (!subjectType) continue;\n // A same_as slot reuses the linked slot's identity (a second wardrobe of the same\n // person); it grounds on that slot's reference, so it needs no sheet of its own.\n if (slot.sameAs) continue;\n if (slot.presence.size < 1) continue;\n const sheetId = ids.sheet(slot.id);\n const slotType = slot.type.toLowerCase();\n const isCast = slotType === \"person\" || slotType === \"animal\";\n nodes.push({\n id: sheetId,\n type: \"image_reference_sheet\",\n // The lone dropped ingest is the source; the sheet fans it into a turnaround.\n inputs: { references: [slot.ref] },\n params: {\n model: sheetModel ?? ACTOR_SHEET_MODEL,\n // The clean-plate clause mirrors the frame prompts' CLEAN PLATE block: a sheet\n // that comes back with a fake camera app baked in (P2-22) poisons EVERY frame\n // grounded on it, so the suppression must live on the sheet too.\n subject_description: `${slot.description ?? `the ${slot.type}`} — clean plate: no phone-camera UI chrome, no app interface, no watermarks, no captions or on-image text`,\n subject_type: subjectType,\n // 4K: the sheet packs up to 8 cells (angles + tight face/detail close-ups), and\n // it's the ONE reference every frame grounds on — per-cell sharpness here\n // propagates to every clip, so it's worth the highest tier on this single asset.\n image_size: \"4K\",\n // The sheet is the look that propagates to EVERY grounded frame, so a glossy\n // studio turnaround makes the whole UGC ad read as \"produced\" (the #1 AI tell).\n // Force a flat, real, front-camera look on the cast sheet so the actor stays\n // authentic, not an airbrushed influencer (CCA-02).\n ...(isCast\n ? {\n style:\n \"authentic UGC look: flat, even, natural front-camera lighting — no studio key/rim light, no seamless backdrop, no shallow depth of field; real skin texture and pores, no airbrushing or beauty retouch; true-to-life everyday styling\",\n }\n : {}),\n },\n });\n slot.ref = ref(sheetId, \"sheet\");\n slot.sheetBacked = true;\n }\n}\n\n/**\n * One shared ingest per recurring identity element, wired into every frame it appears in\n * (`slots[i]` is `elements[i]`). An element that already HAS its picture does not get a\n * hole to fill: this is the seam an Avatar arrives through — a cast avatar is exactly the\n * one real source image the slot was asking someone to drop by hand, and its identity\n * sheet is a URL, ingested as one.\n *\n * A person nobody cast is invented, not asked for: a candid portrait generated from their\n * description, which the sheet then fans into a turnaround, so every frame of this ad shows\n * one realistic person. An Avatar is for an actor who must come back across ads; demanding\n * one for everybody on screen made every video with a person wait on a casting session.\n */\nexport function emitElementIngests(slots: ElementSlot[], elements: RecurringElementT[], nodes: NodeDecl[]): void {\n slots.forEach((slot, i) => {\n const element = elements[i];\n if (!element) return;\n const supplied = element.source_image?.trim() ?? \"\";\n if (isInventedPerson(element)) {\n const description = element.description ?? `the ${slot.label.toLowerCase()} in this ad`;\n // A second look of an invented person is drawn from the first look's face, so it is\n // the same person in other clothes — no avatar exists that could match that face.\n const firstLook = element.same_as ? slots[elements.findIndex((e) => e.label === element.same_as)] : undefined;\n nodes.push(inventedPersonNode(slot.id, description, firstLook ? ref(firstLook.id, \"images#0\") : undefined));\n slot.ref = ref(slot.id, \"images#0\");\n return;\n }\n nodes.push({\n id: slot.id,\n type: \"ingest\",\n params: /^https?:\\/\\//i.test(supplied)\n ? { source: \"url\", url: supplied, expect: \"image\" }\n : { source: \"path\", path: supplied || elementTodo(element, slot.label, \"video\"), expect: \"image\" },\n });\n });\n}\n\n/** Tags the portrait of a person nobody cast: a face to fuse into a sheet, never a scene frame. */\nexport const INVENTED_PERSON_ROLE = \"invented_person\";\n\n/** A person nobody cast: no picture supplied, so the scaffold invents them from their description. */\nexport function isInventedPerson(el: Pick<RecurringElementT, \"type\" | \"source_image\">): boolean {\n return el.type.toLowerCase() === \"person\" && !el.source_image?.trim();\n}\n\n/**\n * The invented person's face, rendered on the model avatars are cast on. `firstLook` is the\n * portrait of the same person in their other outfit, when this is a second look of them.\n */\nfunction inventedPersonNode(id: string, description: string, firstLook?: string): NodeDecl {\n const portrait = candidPersonPortraitPrompt({ subject: description });\n return {\n id,\n type: \"image_generate\",\n tags: { role: INVENTED_PERSON_ROLE },\n ...(firstLook ? { inputs: { reference: [firstLook] } } : {}),\n params: {\n model: AD_IMAGE_MODEL,\n ...imageRenderParams(AD_IMAGE_MODEL, { size: \"2K\", quality: \"high\" }),\n prompt: firstLook\n ? `The same person as in the attached photo, in another look. Keep their face, hair, skin and build identical to the attached photo; change only the clothes and styling.\\n${portrait}`\n : portrait,\n aspect_ratio: nearestSupportedAspectRatio(\"image_generate\", AD_IMAGE_MODEL, \"3:4\"),\n },\n };\n}\n\n/** The element slots present anywhere in this scene (either edge) — for the motion brief. */\nexport function slotsForScene(slots: ElementSlot[], sceneIndex: number): ElementSlot[] {\n return slots.filter((s) => s.presence.has(sceneIndex));\n}\n\n// Reference ORDER matters for gpt-image: only the first reference gets \"extra richness\"\n// in texture, so the most identity-critical asset (the presenter, then the hero product,\n// then the logo) must lead. Lower rank = earlier. Stable within a rank.\nconst REFERENCE_RANK: Record<string, number> = { person: 0, animal: 0, product: 1, logo: 2, badge: 3, location: 4 };\nfunction referenceRank(type: string): number {\n return REFERENCE_RANK[type.toLowerCase()] ?? 5;\n}\n/** Order a scene's slots identity-critical first (presenter → product → logo → …). */\nexport function orderSlotsByIdentity(slots: ElementSlot[]): ElementSlot[] {\n return [...slots].sort((a, b) => referenceRank(a.type) - referenceRank(b.type));\n}\n","import type { NodeTags } from \"../../schema/canvas.ts\";\nimport { sanitizeId } from \"../lib/elements.ts\";\n\n/**\n * Every node id the video scaffold emits, built in one place.\n *\n * An id is an address, not a label: other nodes `$ref` it, the validator reads roles and\n * scenes off it on canvases written before nodes carried tags, an operator edits \"the\n * s3_start frame\" by name, and the content-addressed cache keys a billed node by what it\n * references. Two call sites spelling the same id by hand is how one of them drifts.\n *\n * A composited scene's region nodes carry a `_r<n>` suffix after the scene index (`s2_r1_clip`),\n * so a split-screen panel's frames never collide with the scene's or each other's.\n */\nfunction regionTag(region: number | undefined): string {\n return region === undefined ? \"\" : `_r${region}`;\n}\n\nexport type Edge = \"start\" | \"end\";\n\nexport const ids = {\n /** The baked blueprint, and the slim projection of it every frame reads. */\n prompt: \"prompt\",\n promptStyle: \"prompt_style\",\n spine: \"spine\",\n overlaid: \"overlaid\",\n captions: \"captions\",\n captionsVoiceMix: \"captions_voice_mix\",\n captionsTranscribe: \"captions_transcribe\",\n captionsTranscript: \"captions_transcript\",\n audioMix: \"audio_mix\",\n final: \"final\",\n musicBed: \"music_bed\",\n /** A scene's generated keyframe for one edge. */\n frame: (scene: number, edge: Edge, region?: number): string => `s${scene}${regionTag(region)}_${edge}`,\n /** The ingest of a scene's real source frame for one edge. */\n sourceFrame: (scene: number, edge: Edge, region?: number): string => `s${scene}${regionTag(region)}_${edge}_ref`,\n clip: (scene: number, region?: number): string => `s${scene}${regionTag(region)}_clip`,\n clipTrim: (scene: number, region?: number): string => `s${scene}${regionTag(region)}_clip_trim`,\n voiceExtract: (scene: number): string => `s${scene}_voextract`,\n ambient: (scene: number): string => `s${scene}_ambient`,\n key: (scene: number): string => `s${scene}_key`,\n composite: (scene: number): string => `s${scene}_composite`,\n screenRef: (scene: number): string => `s${scene}_screen_ref`,\n graphicRef: (scene: number): string => `s${scene}_graphic_ref`,\n sfx: (scene: number, k: number): string => `s${scene}_sfx${k}`,\n /** One recurring element's ingest slot, deduped by the caller. */\n element: (label: string, index: number): string => sanitizeId(`el_${label}`, `el_${index}`),\n sheet: (slotId: string): string => `${slotId}_sheet`,\n /** One speaker's casting, deduped by the caller. */\n voice: (speaker: string, ordinal: number): string => sanitizeId(`voice_${speaker}`, `voice_${ordinal}`),\n /** One read of a phrase, deduped by the caller. */\n phraseRead: (phrase: number, speaker: string): string => sanitizeId(`vo_ph${phrase}_${speaker}`, `vo_ph${phrase}`),\n /** A speaker's on-camera extracts laid on one track, and that track re-voiced. */\n voiceTrack: (voice: string): string => `${voice}_track`,\n voiceConv: (voice: string): string => `${voice}_conv`,\n} as const;\n\n/** A `$ref` to one output of a node. */\nexport function ref(id: string, output: string): string {\n return `$ref:${id}.${output}`;\n}\n\n/** What a scene node is, stated on the node: its scene, its region, its job in the assembly. */\nexport function sceneTags(scene: number, role: string, region?: number): NodeTags {\n return { scene, ...(region === undefined ? {} : { region }), role };\n}\n","import { type CompositionRegion, VideoBlueprintSchema, type VideoScene } from \"@baker/canvas-contract\";\n\n// Layouts the scaffold reproduces by building one clip per region and compositing\n// them. `full_frame` (or anything else) stays the single-shot path.\nconst COMPOSITE_LAYOUTS = new Set([\"split_screen\", \"pip\", \"keyed_overlay\"]);\n\n// A region whose content is a SCREEN/UI surface — an app, website, chat, dashboard, or\n// phone mockup. These must NEVER be generated by the video model: Seedance hallucinates\n// garbled UI text and non-brand chrome, and stacking one leaves a visible seam. They are\n// composited as a REAL screenshot or a brand HTML hyperframe on the overlay layer instead.\n// Strong UI signals only — bare \"screen\"/\"phone\" are excluded so a keyed \"green screen\"\n// presenter or a hand holding a phone isn't misread as an interface to composite.\nexport const UI_SURFACE_RE =\n /\\b(?:app|ui|web ?site|web ?page|website|browser|chat|interface|mock-?up|in[- ]?app|dashboard|app screen|phone screen|screen[- ]?(?:recording|capture|grab|share))\\b/i;\n// The deconstruct's typed `kind` is authoritative when present: screen_capture and\n// static_graphic are real-surface regions (screenshot / HTML rebuild), camera and\n// generated are footage. The prose regex is only the fallback for older blueprints —\n// and it reads panel + summary ONLY: a region's frame_prompt routinely carries the\n// clean-plate clause (\"no text or UI overlays\"), whose \"UI\" token made the PRESENTER\n// region classify as a UI surface (audit defect A4: the phone stub was labeled with\n// the presenter's description).\nexport function regionIsUiSurface(r: CompositionRegion): boolean {\n const kind = (r.kind ?? \"\").trim().toLowerCase();\n if (kind === \"screen_capture\" || kind === \"static_graphic\") return true;\n if (kind === \"camera\") return false;\n // `generated` is honored only when the prose agrees: the deconstruct flip-flops kinds\n // scene-to-scene (the same Excel panel typed static_graphic in one scene, generated in\n // the next), and trusting a mistyped `generated` re-opened the Seedance-paints-UI hole\n // the kinds exist to close. UI-flavored prose wins; a genuine b-roll region stays.\n return UI_SURFACE_RE.test(`${r.panel ?? \"\"} ${r.summary ?? \"\"}`);\n}\n/** True when every NON-presenter panel is a UI surface (≤1 real non-UI region remains). */\nexport function isUiOnlyComposite(regions: CompositionRegion[]): boolean {\n const ui = regions.filter(regionIsUiSurface).length;\n return ui >= 1 && regions.length - ui <= 1;\n}\n\n/** The composite regions of a scene, or null when it isn't a ≥2-region composite. */\nexport function compositeRegionsOf(scene: VideoScene): CompositionRegion[] | null {\n const comp = scene.composition;\n const layout = (comp?.layout ?? \"\").toLowerCase();\n if (!COMPOSITE_LAYOUTS.has(layout)) return null;\n const regions = (comp?.regions ?? []).filter((r): r is CompositionRegion => Boolean(r) && typeof r === \"object\");\n return regions.length >= 2 ? regions : null;\n}\n\n/**\n * The background-plate region of a UI-routed composite — the presenter/camera panel\n * the single-shot fallback actually renders (the UI panel lives on the overlay layer).\n */\nexport function compositePlateRegion(scene: VideoScene): CompositionRegion | null {\n const regions = compositeRegionsOf(scene);\n if (!regions) return null;\n return regions.find((r) => r.is_presenter) ?? regions.find((r) => !regionIsUiSurface(r)) ?? null;\n}\n\n/**\n * Scene indexes whose composite routes to the OVERLAY layer (real screen/graphic\n * surface, plain background clip) — decided PER PERSISTENT LAYOUT, not per scene.\n * Within a consecutive run of scenes sharing one composition signature (layout + axis\n * + panels), if ANY scene's regions classify UI-only the whole run routes UI-only:\n * the run is one unbroken physical layout, and Gemini merely described the same band\n * with different words scene to scene (\"screen recording\" vs \"landing page\" — the\n * audit's Superchat flip-flop, which AI-generated the document band on half the\n * scenes and stubbed it real on the others).\n */\nexport function uiRoutedSceneSet(input: unknown): Set<number> {\n return new Set(uiRoutedRuns(input).flat());\n}\n\n/**\n * The UI-routed scenes GROUPED into their persistent-layout runs (consecutive scenes\n * sharing one composition signature). Each run is ONE physical surface window — the\n * overlay layer seeds one grouped stub per run (one continuous screen recording with\n * a state timeline), never one stub per scene.\n */\nexport function uiRoutedRuns(input: unknown): number[][] {\n const blueprint = VideoBlueprintSchema.parse(input);\n const info = blueprint.scenes.map((scene) => {\n const regions = compositeRegionsOf(scene);\n if (!regions) return null;\n const comp = scene.composition;\n const panels = regions\n .map((r) => (r.panel ?? \"\").toLowerCase())\n .sort()\n .join(\",\");\n // A verified surface_id DOMINATES the signature: the deconstruct's continuity\n // pass assigns one id per real on-screen surface, so two scenes sharing it are\n // the same surface whatever the per-scene layout label says (Pass-2 flaps\n // pip ↔ keyed_overlay on the same phone insert). Conversely a different id\n // splits the run even under one unchanged layout — a source that splices two\n // distinct screen recordings needs two stubs, one screenshot can't cover both.\n const surfaceIds = regions\n .filter(regionIsUiSurface)\n .map((r) => (r.surface_id ?? \"\").trim())\n .filter(Boolean)\n .sort()\n .join(\",\");\n const sig = surfaceIds\n ? `sid:${surfaceIds}`\n : `${(comp?.layout ?? \"\").toLowerCase()}|${(comp?.split_axis ?? \"\").toLowerCase()}|${panels}`;\n return { sig, ui: isUiOnlyComposite(regions) };\n });\n const runs: number[][] = [];\n let run: number[] = [];\n const flushRun = () => {\n if (run.some((i) => info[i]?.ui)) runs.push(run);\n run = [];\n };\n info.forEach((inf, i) => {\n if (!inf) {\n flushRun();\n return;\n }\n const prev = i > 0 ? info[i - 1] : null;\n if (!prev || prev.sig !== inf.sig) flushRun();\n run.push(i);\n });\n flushRun();\n return runs;\n}\n","import {\n type BlueprintFloatingElement,\n BlueprintFloatingElementSchema,\n type BlueprintOverlay,\n BlueprintOverlaySchema,\n type CompositionRegion,\n type VideoBlueprint,\n VideoBlueprintSchema,\n type VideoScene,\n} from \"@baker/canvas-contract\";\nimport { z } from \"zod\";\nimport { escapeHtml } from \"../lib/html.ts\";\nimport { compositeRegionsOf, regionIsUiSurface, UI_SURFACE_RE, uiRoutedRuns } from \"./routing.ts\";\nimport { SEEDANCE_CEILING_S, type SpineClock, sceneCeilingS, spineClockFor } from \"./timing.ts\";\n\n// --- Overlay HTML generation: a faithful, EDITABLE draft, not a prop renderer. ---\n\n// `--` cannot appear inside an HTML comment (it would close `-->` early and leak\n// the rest as visible text). Collapse any run of dashes so blueprint-supplied\n// strings stay safely inside the comments we emit for floating-element stubs.\nexport function commentSafe(s: string): string {\n return escapeHtml(s).replace(/-{2,}/g, \"–\");\n}\n\n// The composition runtime only understands these canned entrances; every other\n// value (or none) renders as a plain fade. Normalize known synonyms so a\n// reference \"slide\" doesn't silently degrade, and drop anything unsupported\n// instead of emitting a dead `data-anim` the runtime ignores.\nconst SUPPORTED_ANIMS = new Set([\"fade\", \"slide_up\", \"slide_down\", \"pop\"]);\nfunction normalizeAnim(animation: string | undefined): string | undefined {\n if (!animation || animation === \"none\") return undefined;\n const mapped = animation === \"slide\" ? \"slide_up\" : animation;\n return SUPPORTED_ANIMS.has(mapped) ? mapped : undefined;\n}\n\n// Dead-center placements sit ON the talking-head's face. We push them to the bottom\n// band so a seeded overlay never covers the subject (the chips-over-the-cat defect).\nconst FACE_ZONE_POSITIONS = new Set([\n \"center\",\n \"centre\",\n \"mid-center\",\n \"mid-centre\",\n \"middle-center\",\n \"center-center\",\n \"mid\",\n \"middle\",\n]);\n\n/** A 9-grid position string (\"bottom_center\") → a CSS helper class (\"pos-bottom-center\"). */\nfunction positionClass(position: string | undefined): string {\n const p = (position ?? \"bottom_center\").toLowerCase().replace(/[^a-z]+/g, \"-\");\n // The vertical center band is the subject's face — never seed an overlay there.\n const safe = FACE_ZONE_POSITIONS.has(p) ? \"bottom-center\" : p;\n return `pos-${safe}`;\n}\n\ntype CaptionEntry = { text: string; at: number; end: number; ov: BlueprintOverlay };\n\n/**\n * Collect every text overlay across scenes at its ABSOLUTE time and merge a caption the\n * deconstruct repeats in both scenes it spans (identical text, touching windows) into ONE\n * continuous element — otherwise the same words re-animate (flicker) at the cut. Pure.\n */\nfunction collectCaptions(blueprint: VideoBlueprint, clock: SpineClock): CaptionEntry[] {\n return blueprint.scenes\n .flatMap((scene, i) => {\n const sceneStart = scene.start_s ?? 0;\n const overlays = z.array(BlueprintOverlaySchema).safeParse(scene.overlays ?? []);\n return overlays.success\n ? overlays.data\n .filter((ov): ov is BlueprintOverlay & { text: string } => Boolean(ov.text?.trim()))\n .map((ov) => {\n // Spine-clock placement: the overlay rides the PICTURE, so its window is\n // where this scene's picture actually sits, not the reference timestamp.\n const at = clock.map(i, ov.appears_at_s ?? sceneStart);\n return { text: ov.text.trim(), at, end: at + (ov.duration_s ?? 2.5), ov };\n })\n : [];\n })\n .sort((a, b) => a.at - b.at);\n}\n\n// An IDENTICAL caption/graphic re-detected within this gap of its last showing is the\n// deconstruct seeing one continuous element across a cut (or re-detecting it a beat\n// later) — merged/skipped so the render doesn't flicker the same words twice. A\n// deliberate re-show lands well beyond this window.\nconst OVERLAY_REDETECT_GAP_S = 2;\n// A graphic the deconstruct timed under this is a detection artifact no viewer\n// perceives — seeding it just litters the overlay HTML with junk stubs.\nconst OVERLAY_MIN_DUR_S = 0.5;\n\nfunction mergeCaptions(blueprint: VideoBlueprint, clock: SpineClock): CaptionEntry[] {\n // Group by text first — a repeated caption's fragments are interleaved with OTHER\n // captions in time order, so a consecutive-only merge misses them. Within each text,\n // fold windows that touch or re-show within the re-detect gap into one; genuinely\n // far-apart re-shows (a CTA that flashes twice seconds apart) keep their own elements.\n const byText = new Map<string, CaptionEntry[]>();\n for (const e of collectCaptions(blueprint, clock)) {\n const arr = byText.get(e.text);\n if (arr) arr.push(e);\n else byText.set(e.text, [e]);\n }\n const merged: CaptionEntry[] = [];\n for (const arr of byText.values()) {\n let cur: CaptionEntry | null = null;\n for (const e of arr) {\n if (cur && e.at <= cur.end + OVERLAY_REDETECT_GAP_S) cur.end = Math.max(cur.end, e.end);\n else {\n cur = { ...e };\n merged.push(cur);\n }\n }\n }\n return merged.sort((a, b) => a.at - b.at);\n}\n\n/** One text overlay → a positioned, timed, editable element (empty when no text). */\nfunction overlayElement(ov: BlueprintOverlay, at: number, dur: number): string {\n if (!ov.text?.trim()) return \"\";\n const role = ov.role ? ` data-role=\"${escapeHtml(ov.role)}\"` : \"\";\n const normAnim = normalizeAnim(ov.animation);\n const anim = normAnim ? ` data-anim=\"${normAnim}\"` : \"\";\n // Carry the reference's animation note as an escaped attribute (not an HTML\n // comment) so a detail like \"slow --> pop\" can't close the comment early and\n // leak as visible text — and stays machine-readable for a future runtime.\n const detail = ov.animation_detail ? ` data-anim-detail=\"${escapeHtml(ov.animation_detail)}\"` : \"\";\n // `clip` satisfies the hyperframes pre-render gate (a timed element needs\n // class=\"clip\"); the seeded GSAP timeline still drives the entrance/exit, so the\n // two timing systems coexist (proven on the shipped Barkibu render).\n return `<div class=\"ov clip ${positionClass(ov.position)}\" data-start=\"${at}\" data-dur=\"${dur}\"${role}${anim}${detail}>${escapeHtml(ov.text.trim())}</div>`;\n}\n\n// A rich, card-like overlay (notification popup, tweet/X post, chat bubble, comment,\n// review/social-proof card, stat counter) is best reproduced as an ANIMATED Hyperframes\n// block, not a flat static image — it should slide/pop in on its beat. Matched on the\n// element's kind/description so the agent gets the right tool.\nconst RICH_OVERLAY_RE =\n /notif|tweet|\\bx post\\b|post\\b|comment|message|chat|bubble|card|review|rating|stat|counter|toast|popup/;\n\n/** The command that best sources a real asset for a floating element kind. */\nfunction sourceHint(fe: BlueprintFloatingElement): string {\n const desc = fe.brand_name || fe.what_it_represents || fe.description || fe.kind || \"element\";\n const haystack = `${fe.kind ?? \"\"} ${fe.description ?? \"\"} ${fe.what_it_represents ?? \"\"}`.toLowerCase();\n switch ((fe.kind ?? \"\").toLowerCase()) {\n case \"logo\":\n return \"baker images logo <domain> (or baker images library)\";\n case \"emoji\":\n case \"sticker\":\n return `baker images sticker \"${desc}\" (or baker images gif)`;\n case \"product_cutout\":\n return `baker images library \"${desc}\" (the client's own product)`;\n default:\n // A notification/tweet/comment/social-proof CARD animates in — use a real\n // Hyperframes block, not a flat icon: `npx hyperframes catalog --type block`\n // then `add` (e.g. social-proof-card, notification-toast) and nest it.\n if (RICH_OVERLAY_RE.test(haystack)) {\n return `npx hyperframes add <social-card/notification block> for \"${desc}\" (animated overlay, not a static icon — see references/hyperframes/catalog.md)`;\n }\n // arrows, UI chrome, progress bars, badges, generic graphics → flat icon.\n return `baker images icon \"${desc}\"`;\n }\n}\n\n/**\n * One presence window per floating element, in timeline order. Accumulated in two\n * passes: elements are keyed by what+where; a re-detection within the re-detect gap\n * of a window's end EXTENDS that window (the same persistent watermark seen scene\n * after scene), a re-show beyond the gap starts a genuinely separate window. Floating\n * ui_elements that ARE a UI-routed scene's surface are skipped (the grouped surface\n * stub owns them); sub-perceptual flashes are dropped.\n */\ntype FloatWindow = { fe: BlueprintFloatingElement; at: number; end: number };\n\n/** Identity key for cross-scene float dedup: what the element is + where it sits. */\nfunction floatKey(fe: BlueprintFloatingElement): string {\n return [\n (fe.kind ?? \"element\").toLowerCase(),\n (fe.brand_name || fe.what_it_represents || fe.description || \"\").toLowerCase().trim(),\n positionClass(fe.position),\n ].join(\"|\");\n}\n\n/** A floating ui_element that IS a UI-routed scene's surface (the grouped stub owns it). */\nfunction floatIsRoutedSurface(fe: BlueprintFloatingElement, sceneIsUiRouted: boolean): boolean {\n return sceneIsUiRouted && (fe.kind ?? \"\").toLowerCase() === \"ui_element\" && UI_SURFACE_RE.test(fe.description ?? \"\");\n}\n\n/** Extend the element's latest window when this detection continues it, else open a new one. */\nfunction addFloatDetection(\n windows: Map<string, FloatWindow[]>,\n fe: BlueprintFloatingElement,\n at: number,\n end: number,\n): void {\n const wins = windows.get(floatKey(fe)) ?? [];\n const last = wins[wins.length - 1];\n if (last && at <= last.end + OVERLAY_REDETECT_GAP_S) {\n last.end = Math.max(last.end, end);\n return;\n }\n wins.push({ fe, at, end });\n windows.set(floatKey(fe), wins);\n}\n\nfunction collectFloatWindows(blueprint: VideoBlueprint, uiRouted: Set<number>, clock: SpineClock): FloatWindow[] {\n const windows = new Map<string, FloatWindow[]>();\n blueprint.scenes.forEach((scene, i) => {\n const sceneStart = scene.start_s ?? 0;\n const floats = z.array(BlueprintFloatingElementSchema).safeParse(scene.floating_elements ?? []);\n if (!floats.success) return;\n for (const fe of floats.data) {\n const at = clock.map(i, fe.appears_at_s ?? sceneStart);\n const dur = fe.duration_s ?? 2.5;\n if (floatIsRoutedSurface(fe, uiRouted.has(i)) || dur < OVERLAY_MIN_DUR_S) continue;\n addFloatDetection(windows, fe, at, at + dur);\n }\n });\n return [...windows.values()].flat().sort((a, b) => a.at - b.at);\n}\n\n/** One non-text element → a COMMENTED <img> stub (clean by default; agent sources a real image, then uncomments). */\nfunction floatingStub(fe: BlueprintFloatingElement, at: number, dur: number): string {\n // All interpolated into an HTML comment → must be comment-safe (no `--`).\n const kind = commentSafe(fe.kind ?? \"element\");\n const label = commentSafe(fe.brand_name || fe.what_it_represents || fe.description || fe.kind || \"element\");\n const hint = commentSafe(sourceHint(fe));\n const slug =\n (fe.kind ?? \"element\")\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\") || \"element\";\n return [\n `<!-- ${kind}: ${label} @ ${at}s for ${dur}s (${positionClass(fe.position)}). Source a real asset: ${hint} — drop it in this dir and uncomment:`,\n `<img class=\"ov clip ${positionClass(fe.position)}\" src=\"your-${slug}.png\" data-start=\"${at}\" data-dur=\"${dur}\" alt=\"\" /> -->`,\n ].join(\"\\n\");\n}\n\n/**\n * A UI-only-composite scene (an app/website/chat the deconstruct wanted as a split) →\n * a COMMENTED phone-mockup PIP stub: the talking-head plays clean full-frame underneath,\n * and the real UI rides on top as a phone PIP sourced from a REAL screenshot or a brand\n * HTML block — never Seedance. Names the exact sourcing command so the operator just\n * drops the asset and uncomments. Empty when the scene isn't a UI composite.\n */\nfunction uiSurfaceStub(blueprint: VideoBlueprint, run: number[], clock: SpineClock): string {\n const scenes = run.map((i) => blueprint.scenes[i]).filter((s): s is VideoScene => Boolean(s));\n if (scenes.length === 0) return \"\";\n // The surface region: the one classified UI, else (on a scene whose prose didn't\n // trip the classifier but whose layout RUN is UI-routed) the non-presenter panel.\n const surfaceOf = (scene: VideoScene): CompositionRegion | undefined => {\n const regions = compositeRegionsOf(scene) ?? [];\n return regions.find(regionIsUiSurface) ?? regions.find((r) => !r.is_presenter);\n };\n const ui = scenes.map(surfaceOf).find(Boolean);\n // Spine-clock window: from the run's first scene's picture start to the last's end.\n const [firstScene] = run;\n const lastScene = run.at(-1);\n if (firstScene === undefined || lastScene === undefined) return \"\";\n const at = clock.startOf(firstScene);\n const end = clock.startOf(lastScene + 1);\n const dur = Math.max(0.5, Math.round((end - at) * 100) / 100);\n const label = commentSafe(ui?.summary || ui?.frame_prompt || ui?.panel || \"the app screen\");\n // The surface's state timeline: one beat per scene, so the operator can produce ONE\n // continuous screen recording (or scripted phone-scroll) that hits every state.\n const states =\n scenes.length > 1\n ? run.map((i) => {\n const s = blueprint.scenes[i] as VideoScene;\n const beat = surfaceOf(s)?.summary || s.summary || \"…\";\n return ` @ ${clock.startOf(i)}s — ${commentSafe(beat)}`;\n })\n : [];\n // A designed panel (kind static_graphic) has no live page to screenshot — the\n // operator rebuilds it as brand HTML or supplies the design asset. Only a real\n // app/site screen gets the screenshot instruction.\n const isGraphic = scenes.some((s) => (compositeRegionsOf(s) ?? []).some((r) => r.kind === \"static_graphic\"));\n const sourcing = isGraphic\n ? [\n \" Build it as a real designed panel; never AI-generate its text/typography:\",\n \" hand-build a brand-accurate HTML block in this composition (restyle the seeded\",\n \" overlay text, or `npx hyperframes add <block>` for a ready-made card),\",\n \" — OR drop the design asset (export it from the brand kit) as panel.png here;\",\n \" then nest it as a timed clip:\",\n ]\n : [\n \" Build it as a real surface, never AI: capture the live page —\",\n \" baker images screenshot https://<brand-domain>/<path> (image-library skill)\",\n \" — OR hand-build a brand-accurate HTML screen; then frame it in a phone mockup:\",\n \" npx hyperframes add phone-scroll (writes compositions/phone-scroll.html)\",\n \" drop the screenshot as screenshot.png in this dir and nest it as a PIP clip:\",\n ];\n const kindLabel = isGraphic ? \"GRAPHIC PANEL\" : \"PHONE UI\";\n const noun = isGraphic ? \"the designed graphic panel this window shows\" : \"the app/site screen this window shows\";\n return [\n `<!-- ${kindLabel} @ ${at}s for ${dur}s — ${noun}: ${label}.`,\n ...(states.length > 0 ? [\" ONE continuous surface; its states over the window:\", ...states] : []),\n ...sourcing,\n ` <div class=\"clip\" data-composition-src=\"compositions/phone-scroll.html\" data-start=\"${at}\" data-duration=\"${dur}\" data-track-index=\"2\" data-width=\"1080\" data-height=\"1920\"></div> -->`,\n ].join(\"\\n\");\n}\n\n// Overlay roles that ARE transcript captions (karaoke/subtitle fragments). With an\n// active caption track they must NOT also seed as frozen static overlays — the track\n// re-creates the words word-synced from the rendered audio, and seeding both rendered\n// the same text twice (the audit's double-caption defect).\nconst CAPTION_ROLE_RE = /^(?:caption|captions|subtitle|subtitles|karaoke)$/i;\n\n/**\n * Build the overlay layer as REAL, hand-editable HTML — the faithful first draft\n * of the reference's on-screen graphics, NOT props fed to a styling engine. Each\n * overlay/floating element becomes a plain element with its text, a position class,\n * and `data-start`/`data-dur`/`data-anim` for the generic timeline runtime. The\n * operator opens index.html and paints: restyle, regroup, add a real <img> bug,\n * build a ticker — full control. The runtime only decides WHEN things show, never\n * HOW they look. Returned as a string the command injects at the composition's\n * `<!--OVERLAYS-->` marker.\n */\nexport function buildOverlayHtml(input: unknown, opts: { captionsActive?: boolean; videoModel?: string } = {}): string {\n const blueprint = VideoBlueprintSchema.parse(input);\n // Overlay windows are seeded on the SPINE clock (where each scene's picture actually\n // sits in the butted concat) so they land on their beats even when the reference had\n // dead air between scenes or a scene was clamped to the clip ceiling. The ceiling is the\n // video model's, so pass the one the canvas renders on or a clamped scene shifts them.\n const clock = spineClockFor(blueprint, opts.videoModel ? sceneCeilingS(opts.videoModel) : SEEDANCE_CEILING_S);\n const blocks: string[] = [\n [\n \"<!-- ⬇ OVERLAY LAYER — this is YOUR HTML to paint, and it is a REAL Hyperframes\",\n \" composition (our hyperframe_render runs the genuine `npx hyperframes` renderer).\",\n \" The reference's overlays are seeded below as plain elements (text + position\",\n \" class + data-start/data-dur). Restyle freely in <style>, regroup, animate, swap a\",\n \" logo placeholder for a real <img> you drop in this dir. But you can do FAR more:\",\n \" • pull a ready-made block — `npx hyperframes add <id>` (lower-third, social card,\",\n \" stat counter, chart, code, logo sting) — and nest it as a\",\n ' <div data-composition-src=\"compositions/<id>.html\" data-start data-track-index ...> clip;',\n \" • drive a real caption track off the transcript (karaoke / themed / kinetic);\",\n \" • animate with the named GSAP motion-rule vocabulary, not just fade/slide/pop;\",\n \" • frame a talking head as a video-call / PIP / split.\",\n \" See ../.claude/skills/creative-canvas-ads/references/hyperframes/ (start at README.md).\",\n \" Positions: edit the .pos-* classes or add your own. -->\",\n ].join(\"\\n\"),\n ];\n\n // Windows are clamped to the video end: an element seeded past `clock.totalS`\n // simply never hides (\"stuck last overlay\") and reads as a render bug.\n const clampDur = (at: number, end: number): number =>\n Math.max(0.1, Math.round((Math.min(end, clock.totalS) - at) * 1000) / 1000);\n const ovParts = mergeCaptions(blueprint, clock)\n .filter((e) => !(opts.captionsActive && CAPTION_ROLE_RE.test(e.ov.role ?? \"\")))\n .map((e) => overlayElement(e.ov, e.at, clampDur(e.at, e.end)))\n .filter(Boolean);\n if (ovParts.length > 0) blocks.push(ovParts.join(\"\\n\"));\n\n // The SAME graphic re-detected across scenes (a persistent watermark, a graphic that\n // spanned the cut or popped back a beat later) must seed ONE stub spanning the UNION\n // of its detections — emitting on first sight kept only the first scene's slice, so\n // a whole-video logo bug stubbed as a sub-second flash.\n const runs = uiRoutedRuns(blueprint);\n const uiRouted = new Set(runs.flat());\n const floatParts = collectFloatWindows(blueprint, uiRouted, clock)\n .map((w) => floatingStub(w.fe, w.at, clampDur(w.at, w.end)))\n .filter(Boolean);\n if (floatParts.length > 0) blocks.push(floatParts.join(\"\\n\"));\n\n // ONE grouped stub per persistent screen surface (per UI-routed layout run): the\n // surface is one continuous recording with a state timeline, not N per-scene\n // screenshots — the audit's per-scene stubs asked the operator for 7 screens of\n // what is a single 18s screen recording.\n for (const run of runs) {\n const stub = uiSurfaceStub(blueprint, run, clock);\n if (stub) blocks.push(stub);\n }\n\n return blocks.join(\"\\n\\n\");\n}\n","/** Escape text for an HTML text node or a double-quoted attribute. */\nexport function escapeHtml(s: string): string {\n return s.replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\").replace(/\"/g, \"&quot;\");\n}\n","import {\n clipDurationsFor,\n SEEDANCE_DURATIONS,\n type VideoBlueprint,\n VideoBlueprintSchema,\n type VideoScene,\n} from \"@baker/canvas-contract\";\nimport { estSpeechS } from \"../../lib/speech.ts\";\n\n// Default length of a reproduced scene transition. Real ad transitions are brief;\n// keeping it short bounds how much footage the xfade overlap consumes.\nconst TRANSITION_DEFAULT_S = 0.4;\n// DECON transition type → the ffmpeg `xfade` transition it reproduces. Types not\n// in this map (cut, match_cut, none, other) stay HARD CUTS — a plain concat —\n// because a match cut blended by xfade would lose its seamless-cut effect.\nconst XFADE_BY_TYPE: Record<string, string> = {\n fade: \"fade\",\n dissolve: \"dissolve\",\n whip: \"smoothleft\",\n swipe: \"wipeleft\",\n zoom: \"zoomin\",\n};\n\n/**\n * The model-allowed GENERATION length for a scene: the smallest allowed clip duration\n * in `set` that is >= the real scene duration (capped at the set's max). We always\n * generate AT LEAST the scene length so we can trim the clip back down to the exact\n * scene duration afterwards — generating shorter (what nearest-snap does for e.g. a 5s\n * scene → 4s) would leave a gap that can't be filled. This is what keeps the\n * concatenated picture on the same timeline as the absolute-timed audio. Each video\n * model has its OWN duration set (Seedance ≠ Veo's {4,6,8}), so it is parameterized.\n */\nexport function ceilToDurations(set: readonly number[], durationS: number): number {\n const first = set[0] as number;\n const max = set[set.length - 1] as number;\n if (!Number.isFinite(durationS) || durationS <= 0) return first;\n for (const d of set) if (d >= durationS) return d;\n return max;\n}\n\n/**\n * The clip lengths a canvas generates at on `videoModel`: the registry's `duration` enum\n * (Veo's {4,6,8}, Kling's 3-15, Seedance 2.5's 4-30), else Seedance's. The ONE set the\n * scene clamp, the generation length and the report all read: a scene clamped to\n * Seedance's 15s while Omni stops at 10 put a 12s window on the timeline over a 10s clip,\n * and the difference rendered as a black tail.\n */\nexport function clipDurationSet(videoModel: string): readonly number[] {\n return clipDurationsFor(videoModel) ?? SEEDANCE_DURATIONS;\n}\n\n/** The longest clip `videoModel` renders: no scene holds the timeline for longer. */\nexport function sceneCeilingS(videoModel: string): number {\n return Math.max(...clipDurationSet(videoModel));\n}\n\n/** Seedance's ceiling, for the readings that are about the source rather than a model. */\nexport const SEEDANCE_CEILING_S = Math.max(...SEEDANCE_DURATIONS);\n\n/** The real scene length to place on the timeline, clamped to the video model's ceiling. */\nexport function sceneDurationS(scene: VideoScene, ceilingS: number): number {\n const raw = scene.duration_s ?? (scene.end_s != null && scene.start_s != null ? scene.end_s - scene.start_s : 5);\n return Math.min(Math.max(raw, 0.5), ceilingS);\n}\n\n/**\n * THE one clock of the reproduction: the spine. The picture is physically a butted\n * concat of clips of length `sceneDurationS`, so scene i's picture starts at\n * Σ_{j<i} sceneDurationS(j) — regardless of the reference's own scene timestamps.\n * Everything placed at an absolute second (voice, SFX, music, overlays, the mix\n * length) must be expressed on THIS clock, or dead-air gaps / clamped scenes in the\n * blueprint make the picture run ahead of the audio and never recover. `map` is the\n * identity when scenes tile the source exactly (the common case).\n */\nexport type SpineClock = {\n /** Scene i's picture start on the spine: Σ_{j<i} sceneDurationS(j). */\n startOf(scene: number): number;\n /** A blueprint-absolute second inside scene i → its spine second (clamped to the scene's window). */\n map(scene: number, absS: number): number;\n /** A blueprint-absolute second → its spine second (scene located by the blueprint's own windows). */\n mapAbs(absS: number): number;\n /** The spine's total length: Σ sceneDurationS — what the audio mix must be pinned to. */\n totalS: number;\n};\n\nexport function spineClockFor(blueprint: VideoBlueprint, ceilingS: number): SpineClock {\n // Per scene: its start on the REFERENCE clock (falling back to the previous scene's end\n // when the deconstruct omitted start_s, used to translate absolute stamps), its start on\n // the spine, and its length there.\n const windows: Array<{ refStart: number; spineStart: number; dur: number }> = [];\n let cursor = 0;\n let acc = 0;\n for (const s of blueprint.scenes) {\n const dur = sceneDurationS(s, ceilingS);\n const refStart = s.start_s ?? cursor;\n cursor = s.end_s ?? refStart + dur;\n windows.push({ refStart, spineStart: acc, dur });\n acc += dur;\n }\n const totalS = round3(acc);\n const startOf = (scene: number): number => round3(windows[scene]?.spineStart ?? totalS);\n const map = (scene: number, absS: number): number => {\n // A blueprint has at least one scene, so the clamped index always names a window.\n const w = windows[Math.min(Math.max(scene, 0), windows.length - 1)];\n if (!w) return 0;\n return round3(w.spineStart + Math.min(Math.max(absS - w.refStart, 0), w.dur));\n };\n const mapAbs = (absS: number): number => {\n let i = 0;\n windows.forEach((w, j) => {\n if (j > 0 && w.refStart <= absS) i = j;\n });\n return map(i, absS);\n };\n return { startOf, map, mapAbs, totalS };\n}\n\nfunction round3(n: number): number {\n return Math.round(n * 1000) / 1000;\n}\n\n/** Public, parse-tolerant entry for the spine clock (tests, overlay generation). */\nexport function buildSpineClock(input: unknown, videoModel?: string): SpineClock {\n return spineClockFor(VideoBlueprintSchema.parse(input), videoModel ? sceneCeilingS(videoModel) : SEEDANCE_CEILING_S);\n}\n\n/**\n * The outgoing transition to reproduce at scene `i`'s boundary, or null for a hard\n * cut. The LAST scene never has one (nothing follows it). Only recognized non-cut\n * types map to an xfade; everything else is a hard cut.\n */\nfunction sceneOutTransition(scene: VideoScene, isLast: boolean): { xfade: string; dur: number } | null {\n if (isLast) return null;\n const type = scene.transition_out?.type?.toLowerCase();\n const xfade = type ? XFADE_BY_TYPE[type] : undefined;\n return xfade ? { xfade, dur: TRANSITION_DEFAULT_S } : null;\n}\n\n/**\n * Resolve a scene's timeline lengths: its on-timeline duration, the outgoing\n * transition (dropped when the clip ceiling leaves no room for its tail), the trim\n * target (scene + tail), and the generation length (≥ trim and ≥ the spoken line). Pure.\n */\nexport type SceneLengths = {\n /** The scene's length on the timeline. */\n dur: number;\n /** Its outgoing transition, or null for a hard cut. */\n out: { xfade: string; dur: number } | null;\n /** The footage the scene consumes: its length plus the transition tail. */\n trimTarget: number;\n /** The clip length asked of the model: a length it renders, at least `trimTarget` and the line. */\n genDur: number;\n};\n\nexport function sceneTiming(\n scene: VideoScene,\n isLast: boolean,\n // The line the video model voices in this scene, when it voices one: the clip must be\n // long enough to say it.\n nativeLine: string | undefined,\n // The clip's video model duration set (`clipDurationSet`): Veo's {4,6,8} differs from\n // the Seedance set, so the generation length must snap to the RIGHT set (a Seedance-only\n // length is a hard provider error on Veo), and the scene is clamped to that set's max.\n durationSet: readonly number[],\n): SceneLengths {\n const dur = sceneDurationS(scene, Math.max(...durationSet));\n let out = sceneOutTransition(scene, isLast);\n let trimTarget = dur + (out?.dur ?? 0);\n // A scene at the clip ceiling has no room for the transition tail: the xfade overlap\n // would run past the clip end and drift the picture off the audio timeline. Fall back\n // to a hard cut when the tail can't fit.\n if (out && ceilToDurations(durationSet, trimTarget) < trimTarget) {\n out = null;\n trimTarget = dur;\n }\n const speech = nativeLine === undefined ? 0 : estSpeechS(nativeLine);\n const genDur = ceilToDurations(durationSet, Math.max(trimTarget, speech));\n return { dur, out, trimTarget, genDur };\n}\n\n// Seedance's API max clip is 15s, but the generation gateway frequently times out\n// (HTTP 524) before it can deliver a clip longer than ~10s — the advertised ceiling is\n// not deliverable in practice. So the scaffold never asks for a clip past this SAFE\n// duration: a single shot longer than this splits into the next take (at a scene boundary\n// when the deconstruct pre-split the shot, joined by the shared boundary frame the spine\n// seam-dedups), so every emitted clip stays inside the gateway-deliverable window. 10s is\n// a Seedance-allowed duration, so the split clip still snaps cleanly. (A single scene\n// longer than this that the deconstruct did NOT pre-split can't be split here — the\n// splitter only breaks at scene boundaries — so it's flagged as `report.oversize_scenes`.)\nexport const SEEDANCE_SAFE_MAX_S: number = SEEDANCE_DURATIONS.find((d) => d >= 10) ?? 10;\nexport const PHRASE_MAX_S = SEEDANCE_SAFE_MAX_S;\n","// The only blueprint fields the element-selection prompt reads per scene. Keeping just\n// these lets the (often >256KB) blueprint fit a literal select prompt without dragging in\n// dialogue/overlays/transcript/frame-composition the selector never looks at.\nconst SELECT_SCENE_FIELDS = [\n \"index\",\n \"start_s\",\n \"end_s\",\n \"duration_s\",\n \"summary\",\n \"narrative_role\",\n \"action_detail\",\n \"start_frame_prompt\",\n \"end_frame_prompt\",\n] as const;\nconst SELECT_GLOBAL_FIELDS = [\"cast\", \"branding\", \"voiceover\"] as const;\n\n/**\n * Project a deconstruct blueprint down to ONLY the fields the element-selection pass\n * reads (global cast/branding/voiceover + a handful of per-scene prompt fields). The\n * full blueprint stays the source of truth for prompt.json and scaffolding; this slim\n * is what gets inlined into the select prompt so a long ad's blueprint never blows the\n * inline-prompt limit. Defensive: returns the input unchanged when it isn't an object,\n * copies only keys that exist, and never throws on an unexpected shape.\n */\nexport function slimBlueprintForSelection(blueprintInput: unknown): unknown {\n if (!blueprintInput || typeof blueprintInput !== \"object\" || Array.isArray(blueprintInput)) return blueprintInput;\n const bp = blueprintInput as Record<string, unknown>;\n const out: Record<string, unknown> = {};\n for (const k of [\"version\", \"source\"] as const) if (k in bp) out[k] = bp[k];\n if (bp.global && typeof bp.global === \"object\" && !Array.isArray(bp.global)) {\n const g = bp.global as Record<string, unknown>;\n const slimG: Record<string, unknown> = {};\n for (const k of SELECT_GLOBAL_FIELDS) if (k in g) slimG[k] = g[k];\n out.global = slimG;\n }\n if (Array.isArray(bp.scenes)) {\n out.scenes = bp.scenes.map((s) => {\n if (!s || typeof s !== \"object\" || Array.isArray(s)) return s;\n const sr = s as Record<string, unknown>;\n const slim: Record<string, unknown> = {};\n for (const k of SELECT_SCENE_FIELDS) if (k in sr) slim[k] = sr[k];\n return slim;\n });\n }\n return out;\n}\n\n/**\n * Project a deconstruct blueprint down to ONLY its GLOBAL, cross-frame identity: the ad\n * spec every boundary frame must stay consistent with (cast identity, palette, brand,\n * typography mood, aspect ratio). This is what `target_blueprint` is meant to be — a\n * demoted SHARED style reference, not the source of any one frame's content (each frame's\n * content already lives verbatim in its own `params.prompt`). The full per-scene array is\n * dropped: a frame node inlining all N scenes' prompts/dialogue/transcript per image is\n * pure waste (a 33-scene blueprint is ~200 KB inlined into every one of a dozen frame\n * prompts) and invites the model to blend in another scene's content. Defensive: returns\n * the input unchanged when it isn't an object; never throws on an unexpected shape.\n */\nexport function slimBlueprintForFrameStyle(blueprintInput: unknown): unknown {\n if (!blueprintInput || typeof blueprintInput !== \"object\" || Array.isArray(blueprintInput)) return blueprintInput;\n const bp = blueprintInput as Record<string, unknown>;\n const out: Record<string, unknown> = {};\n // version+source carry the aspect ratio; global carries style/palette/cast/branding;\n // reference_elements names the recurring cast/logo the frames ground on. Everything a\n // frame needs to stay ON-BRAND — and nothing per-scene.\n for (const k of [\"version\", \"source\", \"global\", \"reference_elements\"] as const) if (k in bp) out[k] = bp[k];\n return out;\n}\n","import { VideoBlueprintSchema, type VideoScene } from \"@baker/canvas-contract\";\nimport { estSpeechWindowS } from \"../../lib/speech.ts\";\nimport { assignElementLabels, elementTodo } from \"../lib/elements.ts\";\nimport { isInventedPerson, presenceOf, RecurringElements } from \"./elements.ts\";\nimport { ceilToDurations, clipDurationSet, SEEDANCE_SAFE_MAX_S, sceneDurationS } from \"./timing.ts\";\n\nexport type VideoReport = {\n scene_count: number;\n // `asset_todo` is null when the slot already has its picture (`source_image`, e.g. a cast avatar's sheet).\n elements: Array<{\n label: string;\n type: string;\n description: string | null;\n scenes: number[];\n asset_todo: string | null;\n source_image?: string;\n }>;\n dialogue: Array<{ scene: number; speaker: string; line: string; voice_description: string | null }>;\n sfx_count: number;\n overlay_count: number;\n // Scenes longer than the video model's longest clip, held on the timeline at that length.\n clamped_scenes: Array<{ scene: number; original_s: number; clip_s: number }>;\n // Scenes whose OWN footage span exceeds the gateway-safe clip ceiling. The phrase\n // splitter only breaks a long run at scene boundaries, so a SINGLE scene longer than the\n // ceiling (typically a silent b-roll shot, or a one-shot monologue) still generates an\n // over-long clip that 524s at the gateway. The scaffold can't split it for you — break it\n // into two scenes in prompt.json. Advisory, not a hard error.\n oversize_scenes: Array<{ scene: number; scene_s: number; clip_s: number }>;\n // Scenes whose estimated spoken length runs well past their window (the scaffold\n // over-stuffed the beat). The line will overrun its scene, pushing the picture off the\n // audio timeline — shorten the copy or lengthen the scene. Advisory, not a hard error.\n overstuffed_scenes: Array<{ scene: number; scene_s: number; est_speech_s: number }>;\n has_music: boolean;\n};\n\n// A scene whose estimated speech exceeds its window by more than this is flagged as\n// over-stuffed: a little overrun is fine (a native line continues a beat past its visual\n// cut), but well past the window the audio timeline drifts and lips fall behind.\nconst OVERSTUFF_RATIO = 1.3;\n\n/**\n * The three clip-length advisories for one scene, appended to the report's lists:\n * clamped (truncated past the video model's longest clip), oversize (a single scene whose\n * own clip exceeds the gateway-safe ceiling: unsplittable, will 524), and overstuffed (the\n * estimated speech overruns the scene window).\n */\nfunction collectClipAdvisories(\n scene: VideoScene,\n i: number,\n durationSet: readonly number[],\n out: {\n clamped: VideoReport[\"clamped_scenes\"];\n oversize: VideoReport[\"oversize_scenes\"];\n overstuffed: VideoReport[\"overstuffed_scenes\"];\n },\n): void {\n const round2 = (n: number) => Math.round(n * 100) / 100;\n const ceilingS = Math.max(...durationSet);\n const original = scene.duration_s ?? 5;\n if (original > ceilingS) out.clamped.push({ scene: i, original_s: original, clip_s: ceilingS });\n // The scene window the clip/speech has to fit in (clamped to the model's ceiling).\n const window = sceneDurationS(scene, ceilingS);\n // A single scene longer than the gateway-safe ceiling can't be split (the splitter only\n // breaks at scene boundaries) — its clip will 524. Flag it so the user can split it.\n if (window > SEEDANCE_SAFE_MAX_S)\n out.oversize.push({ scene: i, scene_s: round2(window), clip_s: ceilToDurations(durationSet, window) });\n const speech = (scene.dialogue ?? []).reduce(\n (s, line) => s + (line.line ? estSpeechWindowS(line.line, line.start_s, line.end_s) : 0),\n 0,\n );\n if (speech > window * OVERSTUFF_RATIO)\n out.overstuffed.push({ scene: i, scene_s: round2(window), est_speech_s: round2(speech) });\n}\n\n/** Human-facing checklist the command prints — what needs review/assets after scaffolding. */\nexport function videoReport(input: unknown, elementsInput: unknown, videoModel: string): VideoReport {\n const blueprint = VideoBlueprintSchema.parse(input);\n const elements = RecurringElements.parse(elementsInput);\n const dialogue: VideoReport[\"dialogue\"] = [];\n const clamped: VideoReport[\"clamped_scenes\"] = [];\n const oversize: VideoReport[\"oversize_scenes\"] = [];\n const overstuffed: VideoReport[\"overstuffed_scenes\"] = [];\n let sfxCount = 0;\n let overlayCount = 0;\n blueprint.scenes.forEach((scene, i) => {\n for (const line of scene.dialogue ?? []) {\n if (line.line) {\n dialogue.push({\n scene: i,\n speaker: line.speaker ?? \"voiceover\",\n line: line.line,\n voice_description: line.voice_description ?? null,\n });\n }\n }\n sfxCount += (scene.sfx ?? []).length;\n overlayCount += (scene.overlays ?? []).length;\n collectClipAdvisories(scene, i, clipDurationSet(videoModel), { clamped, oversize, overstuffed });\n });\n return {\n scene_count: blueprint.scenes.length,\n elements: assignElementLabels(elements, \"video\").map(({ el, label }) => ({\n label,\n type: el.type,\n description: el.description ?? null,\n scenes: [...presenceOf(el).keys()].sort((a, b) => a - b),\n asset_todo: el.source_image?.trim()\n ? null\n : isInventedPerson(el)\n ? \"none: an invented, realistic person generated from the description (pass --avatar to keep one actor across ads)\"\n : elementTodo(el, label, \"video\"),\n ...(el.source_image?.trim() ? { source_image: el.source_image.trim() } : {}),\n })),\n dialogue,\n sfx_count: sfxCount,\n overlay_count: overlayCount,\n clamped_scenes: clamped,\n oversize_scenes: oversize,\n overstuffed_scenes: overstuffed,\n has_music: Boolean(blueprint.global?.music?.music_prompt),\n };\n}\n","/**\n * The language a script is spoken in, as the voice pipeline needs it: the English name\n * `voice_select` filters voices on (\"spanish\"), the ISO 639-1 code ElevenLabs reads numbers\n * by (`language_code`; without it eleven_v3 normalizes text with its English rules, so a\n * French ad's \"6900\" was spoken in English) and the country a regional tag names.\n *\n * Everything is derived from a BCP-47 tag with `Intl`, never from a table of languages: an\n * ad can be in any language, and a declared tag (`voice.language`, a translation's\n * `language`) is the best source there is. With none declared, the script's language is\n * Jev's to decide (`languageQuestion`), over every language `Intl` can name.\n */\n\nimport { languageName, languageRegion, languageTag } from \"../../../language.ts\";\nimport type { JevAnswer, JevQuestion } from \"../../nodes/local/lib/jev.ts\";\n\nexport type SpokenLanguage = { tag: string; iso: string; name: string; region?: { code: string; name: string } };\n\n/** What a BCP-47 tag means to the voice pipeline; null when it names no language. Pure. */\nexport function spokenLanguage(code: string): SpokenLanguage | null {\n const tag = languageTag(code);\n if (!tag) return null;\n const iso = new Intl.Locale(tag).language;\n const name = languageName(iso);\n if (!name) return null;\n const region = languageRegion(tag);\n return { tag, iso, name: name.toLowerCase(), ...(region ? { region } : {}) };\n}\n\nconst NAMES = new Intl.DisplayNames([\"en\"], { type: \"language\", fallback: \"none\" });\nconst LETTERS = \"abcdefghijklmnopqrstuvwxyz\";\n\n/**\n * Every language with an ISO 639-1 code that `Intl` can name (about 190), as Jev's options:\n * generated, so no language is missing because nobody typed it in. Measured: a flat Choice this\n * wide still named the language of one short line (\"Bestel nu.\", \"Zamów teraz.\").\n */\nconst LANGUAGE_OPTIONS: Readonly<Record<string, string>> = (() => {\n // A deprecated code (\"mo\", \"iw\", \"in\") reads the same name as the current one: two \"Romanian\"s split Jev's\n // probability in half and neither ever holds, so only the canonical code is offered, and each name once.\n const options: Record<string, string> = {};\n const named = new Set<string>();\n for (const a of LETTERS) {\n for (const b of LETTERS) {\n const code = `${a}${b}`;\n const name = NAMES.of(code);\n if (!name || name.toLowerCase() === code || named.has(name)) continue;\n if (Intl.getCanonicalLocales(code)[0] !== code) continue;\n named.add(name);\n options[code] = name;\n }\n }\n return options;\n})();\n\n/** At or above this probability, Jev's pick is the script's language. Measured: see docs/decision-model.md. */\nexport const LANGUAGE_HELD = 0.8;\n\n/** The question that names the language `lines` are written in: a Choice over every language. Pure. */\nexport function languageQuestion(lines: readonly string[]): JevQuestion {\n return {\n type: \"choice\",\n instructions: {\n lines: lines.join(\"\\n\"),\n question: \"Which language are `lines` written in? When they mix languages, the one most of the words are in.\",\n },\n criteria: { ...LANGUAGE_OPTIONS },\n };\n}\n\n/** The ISO 639-1 code Jev named with enough probability; undefined below `LANGUAGE_HELD` or with no answer. Pure. */\nexport function languageFromAnswer(answer: JevAnswer | undefined): string | undefined {\n if (!answer || !(\"choice\" in answer)) return undefined;\n return (answer.probabilities[answer.choice] ?? 0) >= LANGUAGE_HELD ? answer.choice : undefined;\n}\n\n/**\n * The code of a language named in English (\"spanish\" → \"es\"). Only for a canvas scaffolded\n * before the voice's language was stored as a tag, whose rebuild context holds the name. Pure.\n */\nexport function languageCodeNamed(name: string): string | undefined {\n const wanted = name.trim().toLowerCase();\n return Object.entries(LANGUAGE_OPTIONS).find(([, n]) => n.toLowerCase() === wanted)?.[0];\n}\n\n/** The voice's language from a canvas's rebuild context: its tag, else the name an older canvas stored. Pure. */\nexport function voiceLanguageOf(opts: { spokenLanguage?: string; voiceLanguage?: string }):\n | {\n name: string;\n iso?: string;\n }\n | undefined {\n const spoken = opts.spokenLanguage ? spokenLanguage(opts.spokenLanguage) : null;\n if (spoken) return { name: spoken.name, iso: spoken.iso };\n const legacy = opts.voiceLanguage?.trim().toLowerCase();\n if (!legacy) return undefined;\n const iso = languageCodeNamed(legacy);\n return iso ? { name: legacy, iso } : { name: legacy };\n}\n","import type { VideoBlueprint } from \"@baker/canvas-contract\";\nimport type { VideoConcatClipInput } from \"../../nodes/local/videoConcat.ts\";\nimport type { NodeDecl } from \"../../schema/canvas.ts\";\nimport type { AudioTrack } from \"./audio.ts\";\nimport { ids, ref } from \"./ids.ts\";\nimport type { ScaffoldVideoOpts } from \"./opts.ts\";\nimport type { SceneClip } from \"./timeline-env.ts\";\nimport type { SpineClock } from \"./timing.ts\";\n\n// The assembly after the scenes: the spine that joins their clips, the overlay layer over\n// it, the audio mix, the burned-in captions, and the final mux. Every node here is\n// emitted once, complete: what each one consumes is decided before it is built, so no\n// node is patched after it is on the canvas.\n\ntype SeamMode = NonNullable<ScaffoldVideoOpts[\"seamDedup\"]>;\n\n/**\n * One scene clip as a `video_concat` clip: its timeline length, how the spine gives it\n * that length (a generated clip cut to its window, a still held, or the input as\n * rendered), its fit, its outgoing transition and whether it repeats the previous\n * clip's splice frame. The spine node owns the ffmpeg that follows from these.\n */\nfunction concatClipOf(c: SceneClip): VideoConcatClipInput {\n const length = c.trim ? \"trim\" : c.still ? \"still\" : undefined;\n return {\n duration_s: c.scene_s,\n ...(length ? { length } : {}),\n ...(c.trim && c.trim.offset_s > 0 ? { offset_s: c.trim.offset_s } : {}),\n ...(c.still?.fit === \"pad\" ? { fit: \"pad\" as const } : {}),\n ...(c.out ? { transition: { type: c.out.xfade, duration_s: c.out.dur } } : {}),\n ...(c.continuesFrame ? { continues_frame: true } : {}),\n };\n}\n\n/**\n * Emit the visual spine: a `video_concat` joining the per-scene clips in order (hard\n * cuts, or xfades where the deconstruct detected a transition). Every input lands on\n * the OUTPUT canvas raster, and each clip's length work (a cut to its scene window, a\n * still held for the scene) rides the node's own per-input chain, so no standalone\n * trim node is needed. Returns the spine video ref.\n */\nexport function buildSpine(\n clips: SceneClip[],\n seam: SeamMode,\n dims: { w: number; h: number },\n nodes: NodeDecl[],\n): string {\n const inputs: Record<string, unknown> = {};\n clips.forEach((c, i) => {\n inputs[`c${i}`] = c.ref;\n });\n nodes.push({\n id: ids.spine,\n type: \"video_concat\",\n tags: { role: \"spine\" },\n inputs,\n params: { width: dims.w, height: dims.h, seam_dedup: seam, clips: clips.map(concatClipOf) },\n });\n return ref(ids.spine, \"video\");\n}\n\n/**\n * Emit the two blueprint ingests: `prompt` (the full deconstruct — the human-editable\n * source of truth, and the artifact the command writes) and `prompt_style` (the SLIM\n * projection every frame reads via target_blueprint — global cast/palette/brand only, no\n * per-scene array, so no image inlines ~200 KB of other scenes to render one frame).\n * `prompt_style` is DERIVED: `baker canvas validate`/`run` regenerate it from prompt.json\n * (syncStyleProjection) whenever they diverge, so operator edits to prompt.json actually\n * reach the frames — the projection is never the editing surface.\n */\nexport function emitBlueprintIngests(opts: ScaffoldVideoOpts, nodes: NodeDecl[]): void {\n nodes.push({\n id: ids.prompt,\n type: \"ingest\",\n params: { source: \"path\", path: opts.blueprintPath ?? \"./prompt.json\", expect: \"json\" },\n });\n nodes.push({\n id: ids.promptStyle,\n type: \"ingest\",\n params: { source: \"path\", path: opts.blueprintStylePath ?? \"./prompt.style.json\", expect: \"json\" },\n });\n}\n\n/**\n * Where the burned-in captions' words come from, or null for an ad with no captions.\n *\n * `transcribe` — the normal path, for an ad with a voice: the RENDERED read is\n * transcribed, so the cards match what was actually said. `static` — for an ad nobody\n * speaks, a transcript supplied as a file: transcribing a music-led ad's near-silence is\n * how one came back captioned \"Thank you.\" for sixteen seconds, and with the script itself\n * the words and beat boundaries are exact. With neither, the ad carries no captions.\n */\nexport type CaptionPlan = { source: \"transcribe\" | \"static\"; transcriptRef: string; wordsPerGroup: number | undefined };\n\nexport function planCaptions(opts: ScaffoldVideoOpts, voTracks: AudioTrack[]): CaptionPlan | null {\n if (!opts.captionsCompositionPath) return null;\n const wordsPerGroup = opts.captionWordsPerGroup || undefined;\n if (voTracks.some((t) => t.kind === \"vo\")) {\n return { source: \"transcribe\", transcriptRef: ref(ids.captionsTranscribe, \"transcript\"), wordsPerGroup };\n }\n if (!opts.staticTranscriptPath) return null;\n return { source: \"static\", transcriptRef: ref(ids.captionsTranscript, \"json\"), wordsPerGroup };\n}\n\n/**\n * The burned-in overlay pass, or null when the reference shows nothing to overlay. The\n * overlay content is NOT passed as props — the command bakes it into the composition's\n * index.html as real, editable HTML/CSS (see buildOverlayHtml) so the operator paints the\n * broadcast look directly; the node composites that HTML over the video. With captions,\n * the same render also draws the caption track (its optional `transcript` input): the\n * caption text is picture-independent, and one headless render is one fewer generational\n * encode than a second pass.\n */\nexport function overlayNode(opts: ScaffoldVideoOpts, background: string, captions: CaptionPlan | null): NodeDecl {\n return {\n id: ids.overlaid,\n type: \"hyperframe_render\",\n tags: { role: \"overlay\" },\n inputs: { background, ...(captions ? { transcript: captions.transcriptRef } : {}) },\n params: {\n composition: opts.overlayCompositionPath,\n ...(captions?.wordsPerGroup ? { words_per_group: captions.wordsPerGroup } : {}),\n },\n };\n}\n\n/**\n * Mix voiceover + SFX + music onto the timeline, the music bed ducked under the voice\n * tracks: when the creator speaks the bed drops and recovers in the gaps, so it stays a\n * true background under the words. Returns the mix ref, or undefined with nothing to mix.\n */\nexport function emitAudioMix(tracks: AudioTrack[], clock: SpineClock, nodes: NodeDecl[]): string | undefined {\n if (tracks.length === 0) return undefined;\n const mixInputs: Record<string, unknown> = {};\n for (const t of tracks) mixInputs[t.slot] = t.ref;\n const musicTrack = tracks.find((t) => t.kind === \"music\");\n const voiceSlots = tracks.filter((t) => t.kind === \"vo\").map((t) => t.slot);\n const duck = musicTrack && voiceSlots.length > 0 ? { duck: { track: musicTrack.slot, against: voiceSlots } } : {};\n nodes.push({\n id: ids.audioMix,\n type: \"audio_timeline\",\n inputs: mixInputs,\n params: {\n tracks: tracks.map((t) => ({\n slot: t.slot,\n start_s: t.start_s,\n ...(t.duration_s !== undefined ? { duration_s: t.duration_s } : {}),\n ...(t.gain_db !== undefined ? { gain_db: t.gain_db } : {}),\n })),\n // Pinned to the SPINE length — the one clock. The reference duration can\n // include dead air the butted picture does not reproduce, and `-shortest`\n // at the final mux silently truncates whichever stream runs longer.\n total_ms: Math.round(clock.totalS * 1000),\n ...duck,\n },\n });\n return ref(ids.audioMix, \"audio\");\n}\n\n/**\n * Every line the ad's voice is asked to say, in the order it is said: the text its captions\n * print when `captionsFromScript` is on. Read off the blueprint, so an edited line re-flows.\n */\nfunction scriptOfRecord(blueprint: VideoBlueprint): string {\n return (\n blueprint.scenes\n .flatMap((scene) =>\n (scene.dialogue ?? []).map((line) => ({\n text: line.line?.trim() ?? \"\",\n at: line.start_s ?? scene.start_s ?? 0,\n })),\n )\n .filter((line) => line.text)\n // Stable, so lines that start together keep the order they were written in.\n .sort((a, b) => a.at - b.at)\n .map((line) => line.text)\n .join(\" \")\n );\n}\n\n/**\n * The caption chain's own nodes. Transcribed captions read the clean VOICE BUS (vo tracks\n * only — no music/SFX, which smear Whisper's word timings) through `captions_transcribe`;\n * static ones ingest the supplied transcript. When an overlay layer is wired it already\n * draws them (see `overlayNode`); otherwise the standalone caption composition renders\n * them over the picture, and that becomes the video.\n */\nexport function emitCaptionChain(\n captions: CaptionPlan,\n env: { blueprint: VideoBlueprint; opts: ScaffoldVideoOpts; clock: SpineClock; ttsLanguageCode?: string },\n tracks: AudioTrack[],\n videoRef: string,\n overlaid: boolean,\n nodes: NodeDecl[],\n): { videoRef: string; videoNode?: string } {\n const { opts } = env;\n if (captions.source === \"transcribe\") {\n const script = opts.captionsFromScript ? scriptOfRecord(env.blueprint) : \"\";\n const voTrackList = tracks.filter((t) => t.kind === \"vo\");\n const voiceMixInputs: Record<string, unknown> = {};\n for (const t of voTrackList) voiceMixInputs[t.slot] = t.ref;\n nodes.push({\n id: ids.captionsVoiceMix,\n type: \"audio_timeline\",\n inputs: voiceMixInputs,\n params: {\n tracks: voTrackList.map((t) => ({\n slot: t.slot,\n start_s: t.start_s,\n ...(t.duration_s !== undefined ? { duration_s: t.duration_s } : {}),\n })),\n total_ms: Math.round(env.clock.totalS * 1000),\n },\n });\n nodes.push({\n id: ids.captionsTranscribe,\n type: \"video_transcribe\",\n inputs: { video: ref(ids.captionsVoiceMix, \"audio\") },\n params: {\n ...(env.ttsLanguageCode ? { language: env.ttsLanguageCode } : {}),\n ...(script ? { script } : {}),\n },\n });\n } else {\n nodes.push({\n id: ids.captionsTranscript,\n type: \"ingest\",\n params: { source: \"path\", path: opts.staticTranscriptPath, expect: \"json\" },\n });\n }\n if (overlaid) return { videoRef };\n // Captions-only ad (no overlay layer wired): the standalone caption composition.\n nodes.push({\n id: ids.captions,\n type: \"hyperframe_render\",\n inputs: { background: videoRef, transcript: captions.transcriptRef },\n params: {\n composition: opts.captionsCompositionPath,\n ...(captions.wordsPerGroup ? { words_per_group: captions.wordsPerGroup } : {}),\n ...(opts.captionCarriesOn?.length ? { carries_on: opts.captionCarriesOn } : {}),\n },\n });\n return { videoRef: ref(ids.captions, \"video\"), videoNode: ids.captions };\n}\n\n/**\n * Mux the audio under the (possibly captioned) picture, LAST, so loudness normalization\n * and the stereo upmix land on the final cut. The raw mix is a quiet mono track (tts +\n * ducked bed) that reads as \"no sound\" in casual players, so it is normalized to the\n * social target (-14 LUFS, -1.5 dBTP) in stereo. `shortest` is safe because the picture\n * and the mix are both pinned to the spine clock, and `canvas validate` proves it.\n */\nexport function emitFinalMux(videoRef: string, audioRef: string, nodes: NodeDecl[]): void {\n nodes.push({\n id: ids.final,\n type: \"av_mux\",\n tags: { role: \"mux\" },\n inputs: { video: videoRef, audio: audioRef },\n params: { loudnorm: { i: -14, tp: -1.5, lra: 11 }, length: \"shortest\" },\n });\n}\n","import {\n ELEVENLABS_MAX_MUSIC_LENGTH_MS,\n MUSIC_BED_MODEL,\n SOUND_EFFECT_MODEL,\n type VideoBlueprint,\n VOICE_CONVERT_MODEL,\n VOICEOVER_MODEL,\n} from \"@baker/canvas-contract\";\nimport type { NodeDecl } from \"../../schema/canvas.ts\";\nimport { ids, ref } from \"./ids.ts\";\nimport type { SpineClock } from \"./timing.ts\";\n\n// The audio models are registry roles (`MODEL_ROLES`), named here under the scaffold's own names.\nexport const FIXED_TTS_MODEL = VOICEOVER_MODEL;\nconst FIXED_SFX_MODEL = SOUND_EFFECT_MODEL;\nconst FIXED_MUSIC_MODEL = MUSIC_BED_MODEL;\n// Re-voices a Seedance talking clip's native audio to one consistent brand voice.\nexport const FIXED_VOICE_CONVERT_MODEL = VOICE_CONVERT_MODEL;\n// The music bed is a background texture under a talking-head, never a foreground\n// layer — it sits well below the voice so the words always win. -12 dB let an\n// acoustic bed crowd the VO; -20 keeps it firmly in the background (and the bed is\n// additionally sidechain-ducked under the voice, see buildSfxMusic).\nconst MUSIC_BED_GAIN_DB = -20;\n// Native b-roll ambient sits even deeper than the music bed — it's world tone, a\n// texture under everything, not a foreground layer (opt-in via opts.ambient).\nexport const AMBIENT_BED_GAIN_DB = -20;\n\n/**\n * The emotional arc across scenes for the music bed — so the bed rises/settles with\n * the message. We deliberately do NOT feed the spoken script in: `elevenlabs/music-v1`\n * is vocal-capable, and handing it the ad's words made it SING them over the\n * voiceover. The bed is an instrumental texture shaped to the arc, nothing more — the\n * voice carries the words (and `force_instrumental` is set on the node).\n */\nfunction musicArcDigest(blueprint: VideoBlueprint): string {\n const roles = blueprint.scenes.map((s) => s.narrative_role).filter((r): r is string => Boolean(r));\n const arc = roles.length > 0 ? roles.join(\" → \") : \"\";\n return arc\n ? `\\n\\nEmotional arc across scenes: ${arc}. Shape the bed's energy to this arc, swelling on the payoff. Purely instrumental — no vocals, no singing, no spoken words.`\n : \"\";\n}\n\n/**\n * The music-bed generation prompt, conditioned on the ad's own script + emotional\n * arc so the bed fits the message. When the deconstruct identified the real reference\n * track (via AudD), also style the bed after its vibe — but NEVER reproduce it (that\n * would be a copyright lift); we generate original music that matches mood/tempo/energy.\n */\nfunction musicBedPrompt(blueprint: VideoBlueprint, musicPrompt: string): string {\n const digest = musicArcDigest(blueprint);\n const track = blueprint.global?.music?.identified_track;\n const title = track?.title?.trim();\n const vibe = title\n ? `\\n\\nReference vibe: the original used \"${title}\"${track?.artist?.trim() ? ` by ${track.artist.trim()}` : \"\"} (identified via AudD). Match its mood, tempo, and energy with ORIGINAL music — do not reproduce the track.`\n : \"\";\n return `${musicPrompt}${digest}${vibe}`;\n}\n\n// `end_s`/`kind` are carried internally for timeline reasoning (overlap checks,\n// lip-sync audio routing); only `slot`/`start_s`/`gain_db` reach the emitted\n// audio_timeline params (its track schema is strict).\nexport type AudioTrack = {\n slot: string;\n ref: string;\n start_s: number;\n end_s?: number;\n // Hard cap on the clip's length in the mix (audio_timeline atrim) — used to bound\n // a converted voice track to the timeline so an oversized STS return can't leak.\n duration_s?: number;\n gain_db?: number;\n kind?: TrackKind;\n};\ntype TrackKind = \"vo\" | \"sfx\" | \"music\" | \"ambient\";\n\n/** Build the SFX and music nodes; return their tracks (music ducked under the voices). */\nexport function buildSfxMusic(blueprint: VideoBlueprint, clock: SpineClock, nodes: NodeDecl[]): AudioTrack[] {\n const tracks: AudioTrack[] = [];\n\n // SFX: one node per scene sound-effect event, placed on the spine clock.\n blueprint.scenes.forEach((scene, i) => {\n (scene.sfx ?? []).forEach((sfx, k) => {\n const text = sfx.sound_effect_prompt ?? sfx.description;\n if (!text) return;\n const id = ids.sfx(i, k);\n const params: Record<string, unknown> = { model: FIXED_SFX_MODEL, text };\n // Clamp to the provider range so the emitted canvas self-validates.\n if (typeof sfx.duration_s === \"number\") params.duration_seconds = Math.min(Math.max(sfx.duration_s, 0.5), 30);\n nodes.push({ id, type: \"sound_effect\", params });\n tracks.push({\n slot: `sfx_s${i}_${k}`,\n ref: ref(id, \"audio\"),\n start_s: clock.map(i, sfx.at_s ?? scene.start_s ?? 0),\n kind: \"sfx\",\n });\n });\n });\n\n const musicPrompt = blueprint.global?.music?.music_prompt;\n if (musicPrompt) {\n const total = clock.totalS;\n // The bed NEVER plays over the hook — the opening line is the scroll-stopper and\n // it must read clean. Start no earlier than the end of scene 1 (the hook window),\n // and honor a later reference entry point if the original came in even later. Only\n // the remaining span is generated, so the timing — and the cost — match.\n const hookEnd = clock.startOf(1);\n const refStart = blueprint.global?.music?.starts_at_s;\n const startAt = Math.min(\n Math.max(refStart != null ? clock.mapAbs(refStart) : 0, hookEnd),\n Math.max(total - 0.5, 0),\n );\n const totalMs = Math.round((total - startAt) * 1000);\n // Clamp to the validated range so very long sources still scaffold; the\n // audio_timeline pads/trims the bed to the real length anyway.\n const musicMs = Math.min(Math.max(totalMs, 3000), ELEVENLABS_MAX_MUSIC_LENGTH_MS);\n nodes.push({\n id: ids.musicBed,\n type: \"music\",\n // force_instrumental: the model is vocal-capable; without this it can SING the\n // mood (and feeding it the script made it sing the ad). The voice owns the words.\n params: {\n model: FIXED_MUSIC_MODEL,\n prompt: musicBedPrompt(blueprint, musicPrompt),\n music_length_ms: musicMs,\n force_instrumental: true,\n },\n });\n // Background level (-20 dB) AND sidechain-ducked under the voice in audio_mix, so\n // the bed drops further whenever the creator speaks and recovers in the gaps.\n tracks.push({\n slot: \"music\",\n ref: ref(ids.musicBed, \"audio\"),\n start_s: startAt,\n gain_db: MUSIC_BED_GAIN_DB,\n kind: \"music\",\n });\n }\n\n return tracks;\n}\n","/** The step-by-step next-steps guide embedded in the canvas (and printed by the command). */\nexport const VIDEO_GUIDE = [\n \"Scaffolded by `baker canvas scaffold-video` — a runnable reproduction of your reference video, built like an editing timeline. It is a sequence of clear SHOTS separated at COMPLETE BREAKS (hard cuts): two adjacent presenter shots at a cut are TWO clips, never glued into one take. What stays continuous is the VOICE — a voiceover narration is ONE read across the b-roll it plays over, and a b-roll CUTAWAY between two on-camera moments leaves the presenter shot continuous with the insert sliced in — and each person keeps ONE brand voice (all their clips' native audio re-voiced in a single per-speaker pass), so timbre holds across the cuts. A presenter shot is ONE Seedance clip (native lip-sync + audio); a pure-voiceover stretch is one ElevenLabs tts read; a sub-2s flash is a still hold. A single shot too long for one clip splits into takes that share a boundary frame — the spine drops the duplicated frame (`--seam-dedup head|tail|off`). Every clip gets a CLEAN-PLATE start AND end keyframe (no baked text), RECAST to your dropped reference assets — Seedance interpolates real in-shot motion between them. Each frame grounds only on its own extracted frame + el_* slots (never another generated frame), so all frames render in PARALLEL (no cross-frame cascade). A SPLIT-SCREEN / PICTURE-IN-PICTURE / KEYED-PRESENTER scene is reproduced as one clip PER REGION, stacked or overlaid (see `metadata.todo.composition`). On-screen text/graphics are a separate HTML overlay layer you paint; audio is the voice + SFX + a ducked music bed, normalized stereo. It is a STARTING POINT, not a locked render: add, delete, reorder, split, merge, or re-time scenes freely — see `metadata.todo.full_flexibility`.\",\n \"\",\n \"WHAT TO DO NEXT:\",\n \"0. RE-CRAFT THE SCRIPT FIRST (don't clone). This reference already won in-market, but copying a video is much harder than a static: the hook is targeting and may not transfer, and the message must become TRUE for our brand. Work the `metadata.todo.script_recraft` checklist — for each scene judge its role (hook/body/CTA), decide keep/cut/reorder/replace, and re-author every line for OUR customer's pain + OUR offer. See `references/script-craft.md` (hook/body/CTA framework) and the `meta-ads-playbook` skill. Most of the work lives here.\",\n \"1. Edit each frame's prompt IN PLACE. Every `s<i>_start` keyframe node has its OWN self-contained `params.prompt` (the FRAME DESCRIPTION) — editing one changes only that frame. Rewrite the cast, product, claims, palette into the ad you want. The frame is RECAST to the el_* reference images you drop (the source ad's people are never reused), so describe pose/action/framing here and let the references carry identity.\",\n \"1b. STORYBOARD FIRST — align the look on the cheap stills before clips bill. Each scene's keyframe IS your storyboard; `metadata.video.motion_board` lays out each scene's frame, time window, spoken line, and the graphics scheduled in it. Lock the keyframes + check each graphic lands on its spoken beat, THEN run the clips (images are cheap, videos aren't; the cache re-bills only what you change). See references/video-reproduce.md.\",\n \"2. Fill each `el_*` ingest `[TODO]` path. A PERSON has no slot: they are invented from their description (a candid portrait the sheet is fused from), or played by a company Avatar cast by re-scaffolding with `--avatar <handle>` when the same actor must recur across ads — never a photo of a real person, whose face the image model would reproduce; every other element is ONE real source image. Each is reused across every frame it appears in, so the same identity stays consistent. `baker canvas run` REFUSES to start until every `[TODO]` slot holds a real source — so this is mandatory, not optional.\",\n \"3. Talking heads are NATIVE: a scene with one on-camera speaker is voiced by the video model itself (the line goes in the clip's prompt; models with a `generate_audio` toggle get it set, and the default model always renders audio), so lips and voice are generated together — no separate tts, no post-hoc lip-sync. Edit the line in the scene's `s<i>_clip` prompt to re-author the words TRUE for your brand. Off-camera narration scenes still use a sequenced `tts` per turn.\",\n \"4. Voice consistency: every native talking clip's audio is re-voiced to ONE brand voice via `audio_voice_convert` (timing preserved → lips stay matched). Confirm the `voice_select` casting (one per speaker) — its `voice_id` is the brand voice; set its gender/language so the voice matches the creator.\",\n \"5. Overlays are REAL HTML you paint. Open `video-overlay-composition/index.html`: the reference's overlays are seeded inside `#overlay-root` as plain elements (text + a `.pos-*` class + `data-start`/`data-dur`). Restyle the CSS freely — build lower-thirds, a ticker, whatever the look needs — and replace a logo placeholder with a real `<img>` you source (`baker images icon/sticker/gif/logo`) and drop in that dir. The runtime only shows/hides by timestamp; it makes no styling decisions. Drop `brand-bold.otf` / `brand-regular.otf` there for on-brand type.\",\n \"6. `baker canvas validate` (proves native-audio + timing for free) then `baker canvas run` (generates many billed image/video/audio assets — not free).\",\n \"\",\n \"CRAFT — raises every clip's realism (one-liners; full rationale in references/video-craft.md):\",\n \"- Iterate cheaply, do NOT brute-force takes: credits are scarce. Strong inputs (locked reference + a precise move-by-move prompt) make 1–2 takes land. When a take misses, change the SMALLEST thing and re-run — the cache re-bills only that node. Images are cheap, videos aren't: fix on the frame/reference, not by re-rolling clips.\",\n \"- Keep clips SHORT (trust the scene-length trim — don't pad) and LOCK THE CAMERA: unmotivated AI drift is the top tell.\",\n \"- One generation = one speaking voice — Seedance can't voice two on-camera speakers in one clip; split a two-speaker beat into separate scenes (or one stays silent). Lip-sync follows the line verbatim; carry delivery and emotion in PROSE around the quoted line, never in [brackets] (Seedance renders brackets literally).\",\n \"- Preview cheap → finalize high-res (never prompt the aspect ratio in prose — the pipeline sets it).\",\n \"- Nail the pack shot (final product hero — motivated move, matched light). Geometry drifting? drop a high-angle SCHEMATIC still as an extra reference (a map beats a paragraph). Before/after (dry→wet) = two separate locked references, not words mid-scene.\",\n \"- The hook reads SOUND-OFF: scene 1's frame + overlay carry it in ~1s — don't bury the hook in the spoken line (meta-ads-playbook §39/§48).\",\n \"\",\n \"Tip: `prompt.json` is the deconstruction provenance + the authoritative SHARED AD SPEC for cast identity, palette, brand, and type cohesion. Frames read it through `prompt.style.json`, a DERIVED projection `baker canvas validate`/`run` regenerate from prompt.json automatically — edit prompt.json, never the derived file. The per-frame editing surface is the frame node's own FRAME DESCRIPTION.\",\n \"Production craft: references/video-craft.md. Hook/message/script-structure layer: references/script-craft.md + the meta-ads-playbook skill.\",\n].join(\"\\n\");\n","import {\n BlueprintFloatingElementSchema,\n BlueprintOverlaySchema,\n type VideoBlueprint,\n type VideoScene,\n} from \"@baker/canvas-contract\";\nimport { z } from \"zod\";\nimport type { NodeDecl } from \"../../schema/canvas.ts\";\nimport { sceneRole } from \"../lib/scene-role.ts\";\nimport type { ElementSlot } from \"./elements.ts\";\nimport { ids } from \"./ids.ts\";\nimport type { SceneRoute, TalkScene, VoSeg } from \"./timeline-env.ts\";\nimport { SEEDANCE_CEILING_S, type SpineClock, sceneDurationS } from \"./timing.ts\";\n\n/** The per-scene route map + node histogram stamped into `metadata.video.graph_stats`. */\nfunction buildGraphStats(\n blueprint: VideoBlueprint,\n routes: Map<number, SceneRoute>,\n nodes: NodeDecl[],\n ceilingS: number,\n) {\n const histogram: Record<string, number> = {};\n for (const n of nodes) histogram[n.type] = (histogram[n.type] ?? 0) + 1;\n const billable = nodes.filter((n) => n.type === \"image_generate\" || n.type === \"video_generate\").length;\n return {\n nodes_total: nodes.length,\n nodes_billable: billable,\n node_types: histogram,\n scene_routes: blueprint.scenes.map((scene, i) => ({\n scene: i,\n route: routes.get(i) ?? \"clip\",\n window_s: Math.round(sceneDurationS(scene, ceilingS) * 100) / 100,\n })),\n };\n}\n\n/**\n * The static timing plan the validator proves: VO segments (no overlap, audio ≈ video\n * length) and talking scenes (each presenter phrase / composite presenter has a\n * native-audio clip + an `audio_voice_convert` node). Computed by `buildTimeline`.\n */\nexport function buildVideoMeta(\n blueprint: VideoBlueprint,\n clock: SpineClock,\n ceilingS: number,\n meta: { vo_segments: VoSeg[]; talking_scenes: TalkScene[]; routes: Map<number, SceneRoute> },\n slots: ElementSlot[],\n nodes: NodeDecl[],\n hasAudioMix: boolean,\n) {\n return {\n // WHY the graph has the nodes it has: one route per scene (the dispatcher's own\n // record) + a node-type histogram. The first thing to read when a canvas looks big.\n graph_stats: buildGraphStats(blueprint, meta.routes, nodes, ceilingS),\n // The SPINE length (Σ scene durations) — what the picture and the mix are pinned to.\n duration_s: clock.totalS,\n // The one-clock contract the validator enforces on the LIVE graph: the spine's\n // summed clip lengths and every audio timeline's total_ms must agree with this.\n timeline: {\n spine_node: ids.spine,\n expected_total_s: clock.totalS,\n ...(hasAudioMix ? { audio_mix_node: ids.audioMix } : {}),\n },\n vo_segments: [...meta.vo_segments]\n .sort((a, b) => a.start_s - b.start_s)\n .map(({ onCamera: _onCamera, ...seg }) => seg),\n talking_scenes: meta.talking_scenes,\n lip_sync_caution: buildLipSyncCaution(meta.vo_segments),\n motion_board: buildMotionBoard(blueprint),\n // The wired recurring-element registry (the reference-completeness check reads\n // `ref`/`label`/`type` to warn when a frame describes an element it doesn't wire).\n elements: slots.map((s) => ({\n ref: s.ref,\n label: s.label,\n type: s.type,\n ...(s.description ? { description: s.description } : {}),\n })),\n clip_spans: buildClipSpans(blueprint, nodes),\n };\n}\n\n/**\n * Per generated `video_generate` node, the natural visual span of the scene it\n * renders (from the blueprint, before any model-duration clamping). The validator\n * compares each against the assigned video model's max clip duration and warns when a\n * span can't fit — e.g. a 9.2s scene on Veo (8s cap), which would truncate. The scene\n * index is the one the clip was tagged with when it was emitted.\n */\nfunction buildClipSpans(blueprint: VideoBlueprint, nodes: NodeDecl[]): Array<{ node: string; span_s: number }> {\n const spans: Array<{ node: string; span_s: number }> = [];\n for (const n of nodes) {\n if (n.type !== \"video_generate\" || n.tags?.scene === undefined) continue;\n const scene = blueprint.scenes[n.tags.scene];\n if (!scene) continue;\n // Seedance's ceiling, not the model's: this is the span the validator measures\n // against the model's max, and clamping it to that max first would hide the warning.\n spans.push({ node: n.id, span_s: Math.round(sceneDurationS(scene, SEEDANCE_CEILING_S) * 100) / 100 });\n }\n return spans;\n}\n\n/**\n * Flag scenes where a presenter's lips may NOT match the audio. A single held shot\n * can host only ONE native (lip-synced) clip — voiceover turns must not overlap, and\n * Seedance generates one continuous clip per shot — so when the SAME on-camera speaker\n * has more turns in that shot (a rapid back-and-forth: \"3000?\" … \"4000?\" with an\n * off-camera \"Plus\" between), the first turn is lip-synced but the rest play as `tts`\n * over the same clip, where the mouth is no longer saying those words. This is an\n * inherent limit of reproducing sparse same-shot dialogue, not a wiring bug. We can't\n * fix it without overlapping audio (breaks the no-overlap invariant) or cutting the\n * held shot into per-turn clips (visible camera jumps). So we SURFACE it: each flagged\n * scene's burned-in captions already show the spoken words, but the agent may prefer to\n * cut away to b-roll over those `tts` lines, or accept the captions carry them.\n */\nfunction buildLipSyncCaution(segments: VoSeg[]): Array<{ scene: number; speaker: string; tts_over_native: string[] }> {\n const out: Array<{ scene: number; speaker: string; tts_over_native: string[] }> = [];\n const byScene = new Map<number, VoSeg[]>();\n for (const s of segments) {\n const arr = byScene.get(s.scene) ?? [];\n arr.push(s);\n byScene.set(s.scene, arr);\n }\n for (const [scene, segs] of [...byScene.entries()].sort((a, b) => a[0] - b[0])) {\n // The speaker(s) lip-synced in this scene (their own on-camera voice).\n const nativeSpeakers = new Set(segs.filter((s) => s.onCamera).map((s) => s.speaker));\n for (const speaker of nativeSpeakers) {\n const ttsOver = segs.filter((s) => !s.onCamera && s.speaker === speaker).map((s) => s.slot);\n if (ttsOver.length > 0) out.push({ scene, speaker, tts_over_native: ttsOver });\n }\n }\n return out;\n}\n\n/** The spoken line(s) inside one scene's window, joined — for the motion board. */\nfunction sceneSpokenText(scene: VideoScene): string | null {\n return (\n (scene.dialogue ?? [])\n .map((d) => d.line?.trim())\n .filter((l): l is string => Boolean(l))\n .join(\" \") || null\n );\n}\n\n/**\n * The MOTION BOARD — the reviewable map of \"which graphic fires on which spoken\n * beat\", built from data the scaffold already computes. Per scene it pairs the\n * scene's time window + its spoken line with every overlay/floating graphic\n * scheduled inside it (and that graphic's absolute `at_s`). Review it BEFORE\n * rendering so each overlay lands on the word it's meant to punctuate — the\n * \"anchor every graphic to a spoken word\" discipline. The scene's keyframe\n * (`s<i>_start`) is this board's STORYBOARD: a cheap, cached still to align look on\n * before clips bill. See references/video-reproduce.md.\n */\nfunction buildMotionBoard(blueprint: VideoBlueprint) {\n const round = (n: number) => Math.round(n * 100) / 100;\n let cursor = 0;\n return blueprint.scenes.map((scene, i) => {\n const start_s = scene.start_s ?? cursor;\n const end_s = scene.end_s ?? start_s + sceneDurationS(scene, SEEDANCE_CEILING_S);\n cursor = end_s;\n const spoken = sceneSpokenText(scene);\n const overlays = z.array(BlueprintOverlaySchema).safeParse(scene.overlays ?? []);\n const floats = z.array(BlueprintFloatingElementSchema).safeParse(scene.floating_elements ?? []);\n const graphics = [\n ...(overlays.success ? overlays.data : [])\n .filter((ov) => ov.text?.trim())\n .map((ov) => ({\n kind: \"text\" as const,\n at_s: round(ov.appears_at_s ?? start_s),\n dur_s: round(ov.duration_s ?? 2.5),\n position: ov.position ?? \"bottom_center\",\n text: ov.text?.trim(),\n })),\n ...(floats.success ? floats.data : []).map((fe) => ({\n kind: \"graphic\" as const,\n at_s: round(fe.appears_at_s ?? start_s),\n dur_s: round(fe.duration_s ?? 2.5),\n position: fe.position ?? \"bottom_center\",\n label: fe.brand_name || fe.what_it_represents || fe.description || fe.kind || \"element\",\n })),\n ].sort((a, b) => a.at_s - b.at_s);\n return {\n scene: i,\n role: sceneRole(scene.narrative_role, i, blueprint.scenes.length),\n window_s: [round(start_s), round(end_s)] as [number, number],\n // A continuation b-roll scene shares the previous scene's end frame as its start\n // (no own `s<i>_start` node), so point the storyboard at that shared keyframe.\n storyboard_frames: [scene.continues_previous && i > 0 ? ids.frame(i - 1, \"end\") : ids.frame(i, \"start\")],\n spoken,\n graphics,\n };\n });\n}\n","import type { ClipIntent } from \"../../lib/prompt-profiles.ts\";\n\n/**\n * Where a scene sits in an ad's persuasion arc, read the three ways the scaffolds need it.\n *\n * One module because the three readings are one idea: the first beat stops the scroll, the\n * last asks for the click, the middle carries the argument. They differ only in what the\n * middle is called and in what a declared role may override, and each difference is a\n * decision a consumer depends on — kept as named functions here rather than re-derived at\n * each call site, where a reproduction and an authored ad had drifted into three spellings.\n */\n\n/** A beat's place in the arc, from its position alone. */\nexport type ArcPosition = \"hook\" | \"body\" | \"cta\";\n\n/**\n * The arc read off position. A one-scene video's only scene is the CTA, not a hook: there\n * is nothing after it for a hook to open.\n */\nexport function positionalRole(index: number, total: number): ArcPosition {\n if (index === 0 && total > 1) return \"hook\";\n if (index === total - 1) return \"cta\";\n return \"body\";\n}\n\n/**\n * A reproduced scene's role: the deconstruct's `narrative_role` when it named one (free text,\n * kept verbatim), else its position. Drives the script re-craft checklist and the motion board.\n */\nexport function sceneRole(narrativeRole: string | undefined, index: number, total: number): string {\n return narrativeRole?.trim() || positionalRole(index, total);\n}\n\n/**\n * An authored beat's role (`scaffold-ad`): positional, because it is positional in every ad\n * of that shape, with the middle split into the problem it raises and the proof that answers\n * it. The first beat is the hook whatever the length — an authored ad always opens on one.\n */\nexport function authoredRole(index: number, total: number): \"hook\" | \"problem\" | \"proof\" | \"cta\" {\n if (index === 0) return \"hook\";\n const role = positionalRole(index, total);\n if (role !== \"body\") return role;\n return index < total / 2 ? \"problem\" : \"proof\";\n}\n\n/** Whether a declared role marks the closing call to action. Exact, not a substring: see `clipIntent`. */\nexport function isCtaRole(narrativeRole: string | undefined): boolean {\n return narrativeRole?.trim() === \"cta\";\n}\n\n/**\n * The persuasion INTENT of a clip, used to key the per-intent param recipe\n * (`clipParamRecipe`). Scene 0 is always the hook; a scene whose `narrative_role`\n * reads as the reveal/hero/product beat earns the cine ceiling, as does one the\n * deconstruct saw moving hard; the CTA beat is its own class; everything else is body.\n * Kept deliberately tolerant — the role is free text from deconstruct, so this\n * matches on substrings.\n */\nexport function clipIntent(narrativeRole: string | undefined, index: number, motionLevel?: string): ClipIntent {\n if (index === 0) return \"hook\";\n const role = (narrativeRole ?? \"\").toLowerCase();\n if (/\\bcta\\b|call to action|offer/.test(role)) return \"cta\";\n if (/hero|reveal|product|payoff|transformation|result/.test(role) || motionLevel === \"dynamic\") return \"hero\";\n return \"body\";\n}\n","import type { CompositionRegion } from \"@baker/canvas-contract\";\nimport { ref } from \"../ids.ts\";\nimport { commentSafe } from \"../overlay-html.ts\";\nimport type { SceneCtx } from \"../timeline-env.ts\";\n\n/**\n * A scene whose picture is a REAL surface the operator supplies (a screen, a designed\n * panel): a `[TODO]` ingest (so the placeholder gate forces it before a billed run), held\n * for the scene length on the spine. NO image_generate / video_generate is emitted, so the\n * model can never paint garbled UI text or a baked sticker.\n *\n * One surface = ONE ingest: consecutive scenes the deconstruct verified as the SAME surface\n * (a shared `surface_id` — e.g. a 3-scene import flow) reuse a single ingest\n * (`env.surfaceIngests`), so the operator supplies it exactly ONCE instead of dropping the\n * same capture into a dozen back-to-back `[TODO]`s (the \"13 identical screen refs\" waste).\n * Distinct surfaces stay distinct.\n */\nexport function emitSurfaceStill(\n ctx: SceneCtx,\n surface: { id: string; fallbackLabel: string; todo: (label: string) => string },\n): void {\n const { scene, env } = ctx;\n const regions = (scene.composition?.regions ?? []).filter(\n (r): r is CompositionRegion => Boolean(r) && typeof r === \"object\",\n );\n const surfaceId = regions.find((r) => r.surface_id)?.surface_id;\n let refId = surfaceId ? env.surfaceIngests.get(surfaceId) : undefined;\n if (!refId) {\n const label = commentSafe((scene.summary || scene.start_frame_prompt || surface.fallbackLabel).slice(0, 120));\n refId = surface.id;\n ctx.out.nodes.push({\n id: refId,\n type: \"ingest\",\n params: { source: \"path\", path: surface.todo(label), expect: \"image\" },\n });\n if (surfaceId) env.surfaceIngests.set(surfaceId, refId);\n }\n // Fit-PADDED on the spine's per-input chain — a real screen/designed graphic must\n // never be cropped (cropping slices UI chrome and designed typography).\n ctx.out.clips.push({\n ref: ref(refId, \"asset\"),\n scene_s: ctx.lengths.dur,\n out: ctx.lengths.out,\n still: { fit: \"pad\" },\n });\n}\n","import type { CompositionRegion, VideoBlueprint, VideoScene } from \"@baker/canvas-contract\";\nimport { ids } from \"../ids.ts\";\nimport type { SceneCtx } from \"../timeline-env.ts\";\nimport { emitSurfaceStill } from \"./surface-still.ts\";\n\nfunction regionsOf(scene: VideoScene): CompositionRegion[] {\n return (scene.composition?.regions ?? []).filter((r): r is CompositionRegion => Boolean(r) && typeof r === \"object\");\n}\n\n/** Every region is designed graphics (some static_graphic, no camera feed at all) —\n * no one can be lip-synced on screen, so any dialogue here is voiceover by definition. */\nexport function sceneIsAllGraphic(scene: VideoScene): boolean {\n const regions = regionsOf(scene);\n if (regions.length === 0 || regions.some((r) => (r.kind ?? \"\").toLowerCase() === \"camera\")) return false;\n return regions.some((r) => (r.kind ?? \"\").toLowerCase() === \"static_graphic\");\n}\n\n/**\n * The full set of scenes rendered as a designed-graphic plate — a FULL-FRAME designed\n * graphic (meme collage, statue-with-logo card, animated marketing art) the deconstruct\n * typed a `static_graphic` region on. Generating one garbles wordmarks (the retro \"Google!\"\n * logo) and loses the designed look; the plate is the REAL design asset (or a brand-HTML\n * rebuild), with text/logos riding the overlay layer.\n *\n * The composition is the authority (it is frame-verified by the continuity checker);\n * element PRESENCE lists are transition-frame-noisy — the presenter \"present\" at a graphic\n * scene because the previous shot's last frames bleed in must not re-route the designed\n * panel to generation. No camera region ⇒ nobody is on screen, so a pip collage that\n * nests the presenter stays on the composite/stub path.\n *\n * Closed over `continues_previous` chains. One physical take is ONE content class —\n * Pass-2 routinely types a graphic's 0.6s transition tail but not its 4.5s body, and\n * splitting one take across Seedance and a real plate is visibly incoherent. Propagation\n * is blocked into a scene that shows a camera feed (a camera region, or an explicitly\n * on-camera dialogue line): a mistagged chain must never swallow a presenter shot.\n */\nexport function fullFrameGraphicScenes(blueprint: VideoBlueprint): Set<number> {\n const scenes = blueprint.scenes;\n const graphic = new Set<number>(scenes.flatMap((s, i) => (sceneIsAllGraphic(s) ? [i] : [])));\n const blocked = (s: VideoScene): boolean => {\n if (regionsOf(s).some((r) => (r.kind ?? \"\").toLowerCase() === \"camera\")) return true;\n return (s.dialogue ?? []).some((d) => d.on_camera === true);\n };\n let changed = true;\n while (changed) {\n changed = false;\n scenes.forEach((s, i) => {\n if (graphic.has(i)) return;\n const chainsToPrev = s.continues_previous && graphic.has(i - 1);\n const next = scenes[i + 1];\n const chainsToNext = Boolean(next?.continues_previous) && graphic.has(i + 1);\n if ((chainsToPrev || chainsToNext) && !blocked(s)) {\n graphic.add(i);\n changed = true;\n }\n });\n }\n return graphic;\n}\n\n/**\n * The `graphic_plate` route: like the screen route, but for a designed graphic panel — the\n * operator supplies or rebuilds the REAL design, with no screenshot instruction (there is\n * no live page).\n */\nexport function emitGraphicScene(ctx: SceneCtx): void {\n emitSurfaceStill(ctx, {\n id: ids.graphicRef(ctx.index),\n fallbackLabel: \"the designed panel\",\n todo: (label) =>\n `[TODO: supply the real designed graphic for \"${label}\" — never AI-generate a designed panel (its typography/wordmarks garble). Rebuild it as brand HTML in video-overlay-composition (the scene's text/logos are already seeded there), export the design from the brand kit, or source stock art; text rides the overlay layer, not this plate. Wire it in as a path relative to this canvas file, never an absolute path]`,\n });\n ctx.out.routes.set(ctx.index, \"graphic_plate\");\n}\n","import type { VideoBlueprint } from \"@baker/canvas-contract\";\nimport type { ElementSlot } from \"../elements.ts\";\nimport type { Edge } from \"../ids.ts\";\n\n// Speaker labels that mean \"narration over the picture,\" never a mouth on screen.\n// Used to decide whether a scene's talking subject should be lip-synced — and, in\n// `collapseVoiceover`, folded into a lone on-camera presenter as the same person's\n// off-camera narration. Adding a label here therefore also makes it foldable: never add one\n// a caller uses to mean a genuinely SECOND voice.\nconst NARRATOR_SPEAKERS = new Set([\n \"voiceover\",\n \"voice_over\",\n \"narrator\",\n \"narration\",\n \"vo\",\n \"announcer\",\n \"off_screen\",\n \"offscreen\",\n \"off-screen\",\n]);\n\n/** Whether the video uses on-screen mouths at all (vs pure narration over picture). */\nexport function onCameraDialogue(blueprint: VideoBlueprint): boolean {\n const mode = blueprint.global?.voiceover?.mode;\n return mode !== \"voiceover\" && mode !== \"none\";\n}\n\n/** Whether the source ad has a person in it anywhere: a deconstructed cast, or a recurring person element. */\nexport function sourceShowsPeople(blueprint: VideoBlueprint, slots: ElementSlot[]): boolean {\n return (blueprint.global?.cast ?? []).length > 0 || slots.some((s) => s.type.toLowerCase() === \"person\");\n}\n\nexport const castIdSet = (blueprint: VideoBlueprint): Set<string> =>\n new Set((blueprint.global?.cast ?? []).map((c) => c.id).filter((id): id is string => Boolean(id)));\n\n/** A speaker is \"on camera\" when the video shows mouths and the speaker is a named cast member. */\nexport function isOnCameraSpeaker(speaker: string, casts: Set<string>, cameraOn: boolean): boolean {\n if (!cameraOn) return false;\n if (NARRATOR_SPEAKERS.has(speaker.toLowerCase())) return false;\n return casts.has(speaker);\n}\n\n/**\n * \"Is the presenter actually ON SCREEN in this scene?\" — distinct from \"is the\n * presenter speaking\". A speaker is a cast member for their whole phrase, but the ad\n * often CUTS AWAY to b-roll (a product, a text card) while they keep narrating. We\n * decide visibility from the presenter ELEMENT's per-scene presence, not the speaker\n * label: only a scene where the element appears may slice its window from the talking\n * clip; a scene where the speaker narrates but the element is absent is b-roll.\n * Matches by `cast_id` when the deconstruct linked one, else falls back to the sole\n * `person` element (the single-presenter case). No presence info ⇒ don't constrain.\n */\nexport function makePresenterPresent(\n slots: ElementSlot[],\n canonical: (s: string) => string,\n // strict = no \"sole person\" fallback and no \"no person slots → assume present\"\n // default. Used to decide whether a SPECIFIC speaker is genuinely on screen (so an\n // OFF-camera voice in a one-presenter scene is NOT counted as a second on-camera\n // speaker). The lenient default keeps the slicing logic working when the deconstruct\n // links no element to a clearly-present presenter.\n opts: { strict?: boolean } = {},\n): (speaker: string, sceneIndex: number) => boolean {\n const personSlots = slots.filter((s) => s.type.toLowerCase() === \"person\");\n const bySpeaker = new Map<string, Map<number, Set<Edge>>>();\n for (const slot of personSlots) if (slot.castId) bySpeaker.set(canonical(slot.castId), slot.presence);\n const solePerson = !opts.strict && personSlots.length === 1 ? (personSlots[0] as ElementSlot).presence : null;\n return (speaker, sceneIndex) => {\n const presence = bySpeaker.get(speaker) ?? solePerson;\n if (!presence) return !opts.strict;\n return presence.has(sceneIndex);\n };\n}\n\n/**\n * One person = one VOICE. The deconstruct sometimes labels the same person's on-camera\n * lines `creator_1` and their off-camera narration `voiceover`. When there is exactly\n * one on-camera presenter, fold narrator labels into that presenter so both resolve to\n * a single voice. (Primary fix is the deconstruct prompt; this is the scaffold safety\n * net for older/mislabeled blueprints.) Returns a speaker → canonical-speaker map fn.\n *\n * A caller that needs two speakers to STAY two — a scaffolded ad, where a cutaway beat is\n * its own shot rather than an insert inside a continuous read — must not name its narrator\n * with one of these labels, because this fold cannot tell that case from the one it exists\n * for. `AD_NARRATOR_ID` is what that looks like: a narrator named so it is nobody's\n * mislabelled second voice.\n */\nexport function collapseVoiceover(blueprint: VideoBlueprint): (speaker: string) => string {\n const casts = castIdSet(blueprint);\n const cameraOn = onCameraDialogue(blueprint);\n const presenters = new Set<string>();\n for (const scene of blueprint.scenes)\n for (const l of scene.dialogue ?? []) {\n const sp = l.speaker ?? \"voiceover\";\n if (isOnCameraSpeaker(sp, casts, cameraOn)) presenters.add(sp);\n }\n const [presenter] = presenters;\n if (presenters.size !== 1 || presenter === undefined) return (s) => s;\n return (speaker) => (NARRATOR_SPEAKERS.has(speaker.toLowerCase()) ? presenter : speaker);\n}\n","import type { BlueprintDialogueLine, VideoBlueprint } from \"@baker/canvas-contract\";\nimport { estSpeechS } from \"../../../lib/speech.ts\";\nimport { sceneIsAllGraphic } from \"../routes/graphic-plate.ts\";\nimport { PHRASE_MAX_S } from \"../timing.ts\";\nimport { castIdSet, isOnCameraSpeaker, onCameraDialogue } from \"./speakers.ts\";\n\n// --- PHRASE/SHOT MODEL: one clip per real SHOT, separated at COMPLETE BREAKS. ---\n//\n// A PHRASE is one clip. The reference is a sequence of clear shots with complete breaks\n// (hard cuts) between them, and that is what the scaffold separates by: two adjacent SHOWN\n// presenter shots at a hard cut become TWO clips, never glued into one invented take just\n// because the speech is continuous. What stays continuous: a VOICEOVER narration is ONE\n// read across the b-roll it plays over, and a b-roll CUTAWAY between two shown scenes leaves\n// the presenter shot continuous (the shown scenes aren't adjacent — the insert is sliced in,\n// the voice plays under it). A presenter-shown phrase is ONE Seedance clip (native lip-sync\n// + audio) voice-converted to the brand voice; a phrase shown NOWHERE is one ElevenLabs\n// `tts` read. A single shot longer than the clip ceiling splits into contiguous takes that\n// share a boundary frame (the spine seam-dedups the duplicate). Composite scenes voice on\n// their own (they break a phrase run).\n\n// Two of a voice's lines closer than this are one breath — kept in one continuous read.\nconst PAUSE_GAP_S = 0.6;\n\n/**\n * True when this SHOWN line starts a new clip because it is a COMPLETE BREAK from the run's\n * current shot: it is on camera, the run already showed a presenter, and its scene is the one\n * IMMEDIATELY after the last shown scene while NOT a `continues_previous` continuation — a\n * hard cut between two distinct presenter shots. A cutaway (a b-roll insert between the two\n * shown scenes) leaves them non-adjacent, so this is false and the shots stay one continuous\n * clip with the insert sliced in. Pure.\n */\nfunction isAdjacentShownCut(\n ln: { shown: boolean; sceneIndex: number },\n lastShownScene: number | null,\n scenes: VideoBlueprint[\"scenes\"],\n): boolean {\n if (!ln.shown || lastShownScene === null) return false;\n return ln.sceneIndex === lastShownScene + 1 && scenes[ln.sceneIndex]?.continues_previous !== true;\n}\n\n/** The running phrase state the break decision reads (a subset of the buildPhrases accumulator). */\ntype PhraseBreakState = {\n speaker: string;\n end: number;\n coverEnd: number;\n clipStart: number;\n lastShownScene: number | null;\n};\n\n/**\n * Whether `ln` starts a NEW phrase/clip rather than extending `cur`: a speaker change, a real\n * audio pause, a complete break between two adjacent shown shots, or the run passing the\n * Seedance clip ceiling (a length-split). See buildPhrases for the full rationale. Pure.\n */\nfunction breaksPhrase(\n cur: PhraseBreakState,\n ln: { speaker: string; shown: boolean; sceneIndex: number; start: number },\n lineCover: number,\n lineClipStart: number,\n scenes: VideoBlueprint[\"scenes\"],\n): boolean {\n return (\n cur.speaker !== ln.speaker ||\n ln.start - cur.end > PAUSE_GAP_S ||\n isAdjacentShownCut(ln, cur.lastShownScene, scenes) ||\n Math.max(cur.coverEnd, lineCover) - Math.min(cur.clipStart, lineClipStart) > PHRASE_MAX_S\n );\n}\n\n// The deconstruct anchors scenes to visual shot cuts, so a sentence sliced mid-word\n// gets its boundary words transcribed into BOTH adjacent scenes. Joining verbatim makes\n// the presenter stutter the seam (\"…en téléconsultation en téléconsultation 7J/7\").\n// Cap the dedup window: a longer repeat is deliberate rhetoric, not a boundary artifact.\nconst JOIN_DEDUP_MAX_WORDS = 4;\n\n/** A word normalized for boundary comparison only (case/punctuation-insensitive). */\nfunction joinKey(word: string): string {\n return word.toLowerCase().replace(/[^\\p{L}\\p{N}]+/gu, \"\");\n}\n\n/**\n * Join a phrase's dialogue lines, deduping the words the deconstruct duplicated across\n * a scene boundary: when the next line OPENS with the same 1–4 words the previous line\n * CLOSED with (compared case/punctuation-insensitively), the duplicate opening is\n * dropped. Pure.\n */\nexport function joinDialogueTexts(texts: string[]): string {\n const out: string[] = [];\n for (const text of texts) {\n const prev = out[out.length - 1];\n if (!prev) {\n out.push(text);\n continue;\n }\n const prevWords = prev.split(/\\s+/).filter(Boolean);\n const nextWords = text.split(/\\s+/).filter(Boolean);\n let overlap = 0;\n const maxK = Math.min(JOIN_DEDUP_MAX_WORDS, prevWords.length, nextWords.length);\n for (let k = maxK; k >= 1; k--) {\n const tail = prevWords.slice(-k).map(joinKey).join(\" \");\n const head = nextWords.slice(0, k).map(joinKey).join(\" \");\n if (tail && tail === head) {\n overlap = k;\n break;\n }\n }\n const rest = nextWords.slice(overlap).join(\" \");\n if (rest) out.push(rest);\n }\n return out.join(\" \");\n}\n\nexport type Phrase = {\n speaker: string;\n start_s: number;\n end_s: number;\n text: string;\n // The scene index of the phrase's first line (for metadata attribution).\n firstScene: number;\n // Scene indices where this voice is SHOWN on camera (their video is sliced from the\n // phrase clip). Empty ⇒ pure-b-roll phrase (no presenter clip; tts voice).\n shownScenes: number[];\n presenterShown: boolean;\n};\n\n/**\n * Fold a phrase with no shot of its own into the one before it, so nothing is READ.\n *\n * A phrase claims the scenes it is shown in; a later phrase whose scenes are all taken\n * gets no clip, and the engine falls back to speaking it with ElevenLabs over the picture.\n * For an avatar that is the wrong trade twice over: the mouth on screen is saying\n * different words (the clip was generated from its own line), and the voice is not theirs.\n * Measured, and the reason this exists: \"ElevenLabs should not generate the audio, because\n * the mouths come out wrong — we have to go all in on the model getting the voice right.\"\n *\n * So the text goes into the clip that DOES own that moment: the model says both lines, one\n * mouth, one voice, no overlay. Only ever applied when the rendered voice is being kept —\n * a genuine voiceover has no mouth to contradict and is still read.\n *\n * Pure, and exported for tests.\n */\nexport function foldUnshownPhrases(phrases: readonly Phrase[], keepRenderedVoice: boolean): Phrase[] {\n if (!keepRenderedVoice) return [...phrases];\n const claimed = new Set<number>();\n const kept: Phrase[] = [];\n for (const phrase of phrases) {\n // A phrase nobody is shown speaking is a genuine VOICEOVER, and it is read — there is\n // no mouth on screen for the words to contradict, which is the entire defect this fold\n // exists to prevent. Folding one moved narration into a presenter's clip and had them\n // say it on camera, words the shot was never generated for. Both docstrings here said\n // so; the code did not, because the `presenterShown` test sat inside the same condition\n // as the scene test and a `false` fell through to the fold. The test that should have\n // caught it used a DIFFERENT speaker, so it passed on the speaker check instead.\n if (!phrase.presenterShown) {\n kept.push(phrase);\n continue;\n }\n const available = phrase.shownScenes.filter((scene) => !claimed.has(scene));\n if (available.length > 0) {\n for (const scene of available) claimed.add(scene);\n kept.push(phrase);\n continue;\n }\n // Backwards to the nearest phrase of the SAME speaker that did get a shot. A different\n // speaker's clip would put these words in somebody else's mouth, which is the defect\n // this is here to avoid, so that phrase is left alone to be read.\n const host = [...kept].reverse().find((candidate) => candidate.speaker === phrase.speaker);\n if (!host) {\n kept.push(phrase);\n continue;\n }\n host.text = joinDialogueTexts([host.text, phrase.text]);\n host.end_s = Math.max(host.end_s, phrase.end_s);\n }\n return kept;\n}\n\n/**\n * Scenes with TWO+ distinct on-camera speakers ACTUALLY ON SCREEN — they can't be one\n * presenter clip (Seedance voices one mouth per clip), so both speakers become tts over a\n * plain (silent) scene clip. The discriminator is on-SCREEN presence, not just \"is a cast\n * member\": a lone presenter trading lines with an OFF-camera voice (an interviewer heard but\n * not shown) must stay NATIVE lip-synced. Element presence refines the set ONLY when there\n * is positive evidence (at least one on-camera speaker explicitly on screen); with no element\n * data we keep the conservative \"every on-camera cast speaker counts\" so a real two-hander\n * still trips the gate. Pure.\n */\nfunction multiSpeakerScenes(\n blueprint: VideoBlueprint,\n casts: Set<string>,\n cameraOn: boolean,\n canonical: (s: string) => string,\n presentStrict: (speaker: string, sceneIndex: number) => boolean,\n): Set<number> {\n const multiSpeaker = new Set<number>();\n blueprint.scenes.forEach((scene, i) => {\n const onCamAll = new Set(\n (scene.dialogue ?? [])\n .map((l) => l.speaker ?? \"voiceover\")\n .filter((sp) => isOnCameraSpeaker(sp, casts, cameraOn)),\n );\n const onCamPresent = [...onCamAll].filter((sp) => presentStrict(canonical(sp), i));\n const effective = onCamPresent.length > 0 ? new Set(onCamPresent) : onCamAll;\n if (effective.size >= 2) multiSpeaker.add(i);\n });\n return multiSpeaker;\n}\n\n/** One spoken line flattened out of the blueprint, tagged with whether its speaker is SHOWN. */\ntype PhraseLine = { sceneIndex: number; speaker: string; shown: boolean; start: number; end: number; text: string };\n\n/**\n * The clip window one line forces the phrase clip to cover: always the spoken end/start, plus\n * — when the line is SHOWN (its video is sliced from the clip) — the whole scene's end/start,\n * so a silent lead-in or tail is still generated. `clipStart`/`cover` feed the ceiling cap. Pure.\n */\nfunction lineClipWindow(ln: PhraseLine, scenes: VideoBlueprint[\"scenes\"]): { cover: number; clipStart: number } {\n if (!ln.shown) return { cover: ln.end, clipStart: ln.start };\n const sc = scenes[ln.sceneIndex];\n const sceneEnd = sc?.end_s ?? sc?.start_s ?? 0;\n return { cover: Math.max(ln.end, sceneEnd), clipStart: Math.min(ln.start, sc?.start_s ?? ln.start) };\n}\n\n/**\n * Flatten every non-composite scene's dialogue into time-sorted lines, tagging each with\n * `shown` (the speaker is a cast member AND actually on screen here, not a cutaway or\n * voiceover). A line with no `end_s` is estimated from its words, NOT the scene end (which\n * would fabricate continuity across a silent b-roll gap and merge two separate phrases). Pure.\n */\nfunction dialogueLines(\n blueprint: VideoBlueprint,\n ctx: {\n compositeScenes: Set<number>;\n multiSpeaker: Set<number>;\n canonical: (s: string) => string;\n casts: Set<string>;\n cameraOn: boolean;\n presenterPresent: (speaker: string, sceneIndex: number) => boolean;\n },\n): PhraseLine[] {\n return blueprint.scenes\n .flatMap((scene, sceneIndex): PhraseLine[] => {\n if (ctx.compositeScenes.has(sceneIndex)) return [];\n return (scene.dialogue ?? [])\n .filter((l): l is BlueprintDialogueLine & { line: string } => Boolean(l.line?.trim()))\n .map((l) => {\n const raw = l.speaker ?? \"voiceover\";\n const text = l.line.trim();\n const start = l.start_s ?? scene.start_s ?? 0;\n const shown =\n l.on_camera !== false &&\n !sceneIsAllGraphic(scene) &&\n isOnCameraSpeaker(raw, ctx.casts, ctx.cameraOn) &&\n !ctx.multiSpeaker.has(sceneIndex) &&\n ctx.presenterPresent(ctx.canonical(raw), sceneIndex);\n return {\n sceneIndex,\n speaker: ctx.canonical(raw),\n shown,\n start,\n end: l.end_s ?? start + estSpeechS(text),\n text,\n };\n });\n })\n .sort((a, b) => a.start - b.start);\n}\n\n/**\n * Group dialogue into PHRASES — a phrase is one clip. A new phrase starts on a speaker\n * change, a real audio pause (> PAUSE_GAP_S), a COMPLETE BREAK between two adjacent SHOWN\n * shots (a hard cut, see isAdjacentShownCut — this is the \"several clear clips separated by\n * complete breaks\" the user wants), when the run would exceed the Seedance clip ceiling (a\n * long shot split for length, rejoined via a shared boundary frame the spine seam-dedups),\n * or across a composite scene (voiced on its own). What it does NOT split: a continuous\n * VOICEOVER narration stays ONE read across the b-roll it plays over, and a cutaway insert\n * (b-roll between two shown scenes) leaves the presenter shot continuous with the insert\n * sliced in — neither is a complete break. Pure.\n */\nexport function buildPhrases(\n blueprint: VideoBlueprint,\n canonical: (s: string) => string,\n compositeScenes: Set<number>,\n presenterPresent: (speaker: string, sceneIndex: number) => boolean,\n // Stricter \"is this speaker genuinely on screen\" (explicit element link only). Used\n // ONLY for the multi-speaker gate so an off-camera voice isn't miscounted as a second\n // on-camera mouth — the lenient presenterPresent still drives slicing.\n presentStrict: (speaker: string, sceneIndex: number) => boolean,\n): Phrase[] {\n const casts = castIdSet(blueprint);\n const cameraOn = onCameraDialogue(blueprint);\n const multiSpeaker = multiSpeakerScenes(blueprint, casts, cameraOn, canonical, presentStrict);\n const lines = dialogueLines(blueprint, {\n compositeScenes,\n multiSpeaker,\n canonical,\n casts,\n cameraOn,\n presenterPresent,\n });\n\n const phrases: Phrase[] = [];\n let cur: {\n speaker: string;\n firstScene: number;\n start: number;\n end: number;\n // The latest time the phrase CLIP must cover: every shown scene's end (its window is\n // sliced from the clip) plus the spoken-line ends. Capped so the clip never exceeds\n // the Seedance ceiling — a longer run splits into the next take at a scene boundary.\n coverEnd: number;\n // The earliest time the clip must start: the first line's start, but pulled back to a\n // shown scene's visual start when its line begins after a silent lead-in. The clip span\n // (coverEnd − clipStart) is what the Seedance ceiling caps.\n clipStart: number;\n texts: string[];\n // The last ON-CAMERA scene this run showed (null until one appears) — the next shown\n // scene breaks a new clip only when it is the immediately-following scene AND a hard cut\n // (so two adjacent presenter shots split, but a cutaway insert between them does not).\n lastShownScene: number | null;\n shown: Set<number>;\n } | null = null;\n const flush = () => {\n if (!cur) return;\n const shownScenes = [...cur.shown].sort((a, b) => a - b);\n phrases.push({\n speaker: cur.speaker,\n start_s: cur.start,\n end_s: cur.end,\n text: joinDialogueTexts(cur.texts),\n firstScene: cur.firstScene,\n shownScenes,\n presenterShown: shownScenes.length > 0,\n });\n cur = null;\n };\n for (const ln of lines) {\n const { cover: lineCover, clipStart: lineClipStart } = lineClipWindow(ln, blueprint.scenes);\n // A new phrase/clip starts on a speaker change, a real audio pause (keeps a voiceover ONE\n // fluid read across b-roll), a complete break between two adjacent SHOWN shots (the \"clips\n // separated by complete breaks\" — NOT fired across a cutaway insert, whose shown scenes\n // aren't adjacent), or the run passing the Seedance ceiling (a length-split rejoined by a\n // shared frame the spine seam-dedups). See breaksPhrase.\n const breakRun = !cur || breaksPhrase(cur, ln, lineCover, lineClipStart, blueprint.scenes);\n if (breakRun || !cur) {\n flush();\n cur = {\n speaker: ln.speaker,\n firstScene: ln.sceneIndex,\n start: ln.start,\n end: ln.end,\n coverEnd: lineCover,\n clipStart: lineClipStart,\n texts: [ln.text],\n lastShownScene: ln.shown ? ln.sceneIndex : null,\n shown: new Set(),\n };\n } else {\n cur.texts.push(ln.text);\n cur.end = Math.max(cur.end, ln.end);\n cur.coverEnd = Math.max(cur.coverEnd, lineCover);\n cur.clipStart = Math.min(cur.clipStart, lineClipStart);\n if (ln.shown) cur.lastShownScene = ln.sceneIndex;\n }\n if (ln.shown) cur.shown.add(ln.sceneIndex);\n }\n flush();\n return phrases;\n}\n","/** Scale-to-fill (crop overflow, never distort) + normalize a clip to a target panel size. */\nfunction fillPanel(label: string, w: number, h: number, out: string): string {\n return `[${label}]scale=${w}:${h}:force_original_aspect_ratio=increase,crop=${w}:${h},setsar=1,fps=30[${out}]`;\n}\n\n/**\n * ffmpeg argv: composite N region clips into ONE split-screen frame. Each clip is\n * scaled to fill its panel (overflow cropped, no distortion) then stacked along the\n * split axis — `vertical` ⇒ top/bottom (vstack), `horizontal` ⇒ left/right (hstack).\n * Inputs are c0..c(N-1); output is the composited scene picture.\n */\nexport function splitStackArgs(\n count: number,\n axis: \"vertical\" | \"horizontal\",\n dims: { w: number; h: number },\n): string[] {\n // Every panel dimension must be EVEN (libx264 yuv420p hard-fails on odd sizes) and\n // the panels must SUM to the canvas (stack output = canvas, or the spine concat gets\n // a mismatched raster). Base panels floor to even; the last absorbs the remainder —\n // which stays even because the canvas dims are all even.\n const span = axis === \"horizontal\" ? dims.w : dims.h;\n const base = Math.floor(span / count / 2) * 2;\n const sizeAt = (i: number): number => (i === count - 1 ? span - base * (count - 1) : base);\n const inputs: string[] = [];\n const filt: string[] = [];\n let labels = \"\";\n for (let i = 0; i < count; i++) {\n inputs.push(\"-i\", `{{in.c${i}}}`);\n const pw = axis === \"horizontal\" ? sizeAt(i) : dims.w;\n const ph = axis === \"vertical\" ? sizeAt(i) : dims.h;\n filt.push(fillPanel(`${i}:v`, pw, ph, `p${i}`));\n labels += `[p${i}]`;\n }\n const stack = axis === \"vertical\" ? \"vstack\" : \"hstack\";\n filt.push(`${labels}${stack}=inputs=${count}[v]`);\n return [...inputs, \"-filter_complex\", filt.join(\";\"), \"-map\", \"[v]\", \"{{out.video}}\"];\n}\n\n// 9-grid position → ffmpeg `overlay` x:y expression (W/H = background, w/h = inset),\n// offset by a uniform margin so the inset never kisses the frame edge.\nfunction overlayXY(position: string | undefined, marginPx: number): { x: string; y: string } {\n const p = (position ?? \"bottom_right\").toLowerCase();\n const x = p.includes(\"left\") ? `${marginPx}` : p.includes(\"right\") ? `W-w-${marginPx}` : \"(W-w)/2\";\n const y = p.includes(\"top\") ? `${marginPx}` : p.includes(\"bottom\") ? `H-h-${marginPx}` : \"(H-h)/2\";\n return { x, y };\n}\n\n/**\n * ffmpeg argv: composite N inset clips over a full-frame background (c0 = background,\n * c1..cN = insets, one `overlay` stage each, folded left). Each inset is scaled to its\n * `wpct` of the frame width (height auto, aspect kept) and overlaid at its 9-grid\n * `position` with a uniform margin. A keyed inset arrives pre-alpha'd (`format=auto`\n * honors its transparency); a plain PIP is an opaque rounded box. Compositing EVERY\n * region matters: each one is a billed generated clip — dropping the 3rd+ region paid\n * for a clip that never reached the frame.\n */\nexport function pipOverlayArgs(\n dims: { w: number; h: number },\n insets: Array<{ position?: string; wpct: number }>,\n): string[] {\n const margin = Math.round(dims.w * 0.04);\n const inputs = [\"-i\", \"{{in.c0}}\"];\n const filt = [fillPanel(\"0:v\", dims.w, dims.h, \"bg\")];\n let cur = \"bg\";\n insets.forEach((inset, k) => {\n inputs.push(\"-i\", `{{in.c${k + 1}}}`);\n const iw = Math.round(dims.w * inset.wpct);\n const { x, y } = overlayXY(inset.position, margin);\n const outLabel = k === insets.length - 1 ? \"v\" : `o${k + 1}`;\n filt.push(`[${k + 1}:v]scale=${iw}:-2,setsar=1,fps=30[fg${k + 1}]`);\n filt.push(`[${cur}][fg${k + 1}]overlay=x=${x}:y=${y}:format=auto[${outLabel}]`);\n cur = outLabel;\n });\n return [...inputs, \"-filter_complex\", filt.join(\";\"), \"-map\", \"[v]\", \"{{out.video}}\"];\n}\n\n/**\n * ffmpeg argv: cut a clip to an exact duration (re-encode for frame accuracy, drop\n * audio). With `offsetS > 0` it cuts the sub-window [offset, offset+duration] — used\n * to slice ONE presenter scene's window out of its phrase's continuous clip. `-ss`\n * is placed AFTER `-i` for frame-accurate seeking (the spine must stay aligned).\n * With `dims` the trim also normalizes the picture to the output canvas (scale-to-fill,\n * center-crop) — how a clip GENERATED at the video model's nearest ratio (e.g. 3:4)\n * lands on the ad's true canvas (e.g. 4:5). Free: the trim re-encodes anyway.\n */\nexport function trimArgs(durationS: number, offsetS = 0, dims?: { w: number; h: number }): string[] {\n return [\n \"-i\",\n \"{{in.clip}}\",\n ...(offsetS > 0 ? [\"-ss\", offsetS.toFixed(3)] : []),\n \"-t\",\n durationS.toFixed(3),\n ...(dims\n ? [\"-vf\", `scale=${dims.w}:${dims.h}:force_original_aspect_ratio=increase,crop=${dims.w}:${dims.h},setsar=1`]\n : []),\n \"-an\",\n \"-c:v\",\n \"libx264\",\n // Preserve the seedance source quality through the trim. libx264 DEFAULTS\n // (crf 23) halve the bitrate (measured 9.57→4.40 Mbps) and band on motion;\n // the spine stream-copies the result, so the loss is permanent without this.\n \"-crf\",\n \"18\",\n \"-preset\",\n \"slow\",\n \"-pix_fmt\",\n \"yuv420p\",\n \"{{out.video}}\",\n ];\n}\n\n/** ffmpeg argv: demux a clip's audio AND trim it to the exact spoken window (optionally\n * skipping a silent lead-in when the clip starts before the line). */\nexport function audioExtractArgs(durationS: number, offsetS = 0): string[] {\n return [\n \"-i\",\n \"{{in.clip}}\",\n ...(offsetS > 0.05 ? [\"-ss\", offsetS.toFixed(3)] : []),\n \"-t\",\n durationS.toFixed(3),\n \"-vn\",\n \"-acodec\",\n \"libmp3lame\",\n \"-q:a\",\n \"2\",\n \"{{out.audio}}\",\n ];\n}\n\n/** ffmpeg argv: a full-frame solid colour clip (lavfi color source — no input file).\n * Exact canvas dims, so no letterbox/grey-bar gap behind the overlay CTA card. */\nexport function colorPlateArgs(durationS: number, dims: { w: number; h: number }, color: string): string[] {\n return [\n \"-f\",\n \"lavfi\",\n \"-i\",\n `color=c=${color}:s=${dims.w}x${dims.h}:r=30`,\n \"-t\",\n durationS.toFixed(3),\n \"-c:v\",\n \"libx264\",\n \"-crf\",\n \"18\",\n \"-preset\",\n \"slow\",\n \"-pix_fmt\",\n \"yuv420p\",\n \"{{out.video}}\",\n ];\n}\n","import type { VideoBlueprint } from \"@baker/canvas-contract\";\nimport type { NodeDecl } from \"../../../schema/canvas.ts\";\nimport { AMBIENT_BED_GAIN_DB, type AudioTrack, FIXED_TTS_MODEL, FIXED_VOICE_CONVERT_MODEL } from \"../audio.ts\";\nimport { audioExtractArgs } from \"../ffmpeg-args.ts\";\nimport { ids, ref, sceneTags } from \"../ids.ts\";\nimport type { NativeTurn, SceneCtx, TimelineEnv, TimelineOut } from \"../timeline-env.ts\";\nimport type { Phrase } from \"./phrases.ts\";\nimport type { VoiceGender } from \"./voice-gender.ts\";\n\n/** A native talking scene's extracted clip audio, awaiting a per-speaker conversion. */\nexport type NativeSegment = { voiceNode: string; ref: string; start_s: number; end_s: number };\n\n/**\n * A native talking scene's audio chain. The video model voices the line in its own clip\n * (Seedance 2.0 generates lip-synced speech NATIVELY when the line is in the prompt with\n * `generate_audio`, so no post-hoc lip-sync); this extracts the clip's spoken window into\n * a segment. The segments are merged PER SPEAKER and converted ONCE downstream\n * (`buildPerSpeakerVoiceConversion`: one brand-voice pass over the whole track, not a\n * separate convert per scene), which preserves timing so the lips stay matched.\n *\n * `clip` is the clip carrying the voice — a phrase's clip, or a composited scene's PRESENTER\n * region — with its generated length and where the line starts inside it.\n */\nexport function emitNativeExtract(\n ctx: SceneCtx,\n turn: NativeTurn,\n clip: { ref: string; lengthS: number; offsetS?: number },\n): void {\n const { index } = ctx;\n const offsetS = clip.offsetS ?? 0;\n // Extract the real SPOKEN window (turn start→end), not a word-count estimate. Padding\n // to the estimate ran each composite scene's voice long and overlapped the next scene's\n // on the merged per-speaker track (echo during fast montages). Bounded by the clip.\n const extractLen = Math.min(Math.max(0.5, turn.end_s - turn.start_s), Math.max(0.5, clip.lengthS - offsetS));\n const extractId = ids.voiceExtract(index);\n ctx.out.nodes.push({\n id: extractId,\n type: \"ffmpeg\",\n tags: sceneTags(index, \"voice_extract\"),\n inputs: { clip: clip.ref },\n params: { args: audioExtractArgs(extractLen, offsetS), outputs: { audio: { kind: \"audio\", ext: \"mp3\" } } },\n });\n // Placement is on the spine clock: the line starts where its scene's picture sits.\n const spineStart = ctx.env.clock.map(index, turn.start_s);\n ctx.out.nativeSegments.push({\n voiceNode: turn.voiceNode,\n ref: ref(extractId, \"audio\"),\n start_s: spineStart,\n end_s: spineStart + extractLen,\n });\n}\n\n/** An opt-in ambient b-roll scene: the clip's diegetic ambience, extracted and laid deep under the bed. */\nexport function emitAmbientExtract(ctx: SceneCtx, clipRef: string): void {\n const { index, lengths } = ctx;\n const ambientId = ids.ambient(index);\n const ambientStart = ctx.env.clock.startOf(index);\n ctx.out.nodes.push({\n id: ambientId,\n type: \"ffmpeg\",\n tags: sceneTags(index, \"ambient_extract\"),\n inputs: { clip: clipRef },\n params: { args: audioExtractArgs(lengths.dur), outputs: { audio: { kind: \"audio\", ext: \"mp3\" } } },\n });\n ctx.out.voTracks.push({\n slot: ambientId,\n ref: ref(ambientId, \"audio\"),\n start_s: ambientStart,\n end_s: ambientStart + lengths.dur,\n gain_db: AMBIENT_BED_GAIN_DB,\n kind: \"ambient\",\n });\n}\n\n/**\n * One speaker's extracted segments → the `audio_timeline` track entries that lay them\n * out, in start order, each capped (`duration_s`) where its natural window would run\n * into the SAME speaker's next segment — otherwise both extracts play at once and the\n * voice doubles into an echo. A deconstruct only has to mistime two windows by 40ms\n * (observed live) for the overlap to ship; the cap makes it structurally impossible.\n */\nexport function sameSpeakerTrackEntries(\n segments: Array<Pick<NativeSegment, \"start_s\" | \"end_s\">>,\n): Array<{ slot: string; start_s: number; duration_s?: number }> {\n const sorted = [...segments].sort((a, b) => a.start_s - b.start_s);\n return sorted.map((s, k) => {\n const next = sorted[k + 1];\n const naturalLen = s.end_s - s.start_s;\n const cap = next ? Math.min(s.end_s, next.start_s) - s.start_s : naturalLen;\n const capped = cap < naturalLen - 0.001;\n return {\n slot: `seg${k}`,\n start_s: s.start_s,\n ...(capped ? { duration_s: Math.round(Math.max(0.1, cap) * 1000) / 1000 } : {}),\n };\n });\n}\n\n/**\n * Re-voice the native talking audio to the brand voice in ONE pass per speaker, over\n * the MERGED track — not a separate convert per scene. For each distinct speaker the\n * scene extracts are placed at their absolute starts on a per-speaker `audio_timeline`\n * (`<voice>_track`), then a single `audio_voice_convert` (`<voice>_conv`) maps the whole\n * track to that speaker's brand voice (speech-to-speech preserves timing, so lip-sync\n * survives). Returns one full-length VO track per speaker for the final mix. The\n * separate extract step is unavoidable: `audio_voice_convert` runs in a Convex action\n * with no ffmpeg and ElevenLabs speech-to-speech takes audio bytes only.\n */\nexport function buildPerSpeakerVoiceConversion(\n segments: NativeSegment[],\n totalMs: number,\n nodes: NodeDecl[],\n /** See `ScaffoldVideoOpts.keepRenderedVoice` — the merged track ships as rendered. */\n keepRenderedVoice = false,\n): AudioTrack[] {\n const bySpeaker = new Map<string, NativeSegment[]>();\n for (const seg of segments) {\n const arr = bySpeaker.get(seg.voiceNode) ?? [];\n arr.push(seg);\n bySpeaker.set(seg.voiceNode, arr);\n }\n const tracks: AudioTrack[] = [];\n for (const [voiceNode, segs] of bySpeaker) {\n // The merged native track carries the `_conv` id whether or not it is converted: it\n // is this speaker's on-camera voice either way, and the mix slot, the vo segments and\n // the talking-scene entries all name it by that id. What marks it as a rendered voice\n // rather than a read one is its `on_camera` tag (and the segments' `onCamera`), never\n // the suffix.\n const convId = ids.voiceConv(voiceNode);\n const trackId = keepRenderedVoice ? convId : ids.voiceTrack(voiceNode);\n // Wire inputs in the SAME start order the track entries use — seg{k} ↔ sorted[k].\n const sorted = [...segs].sort((a, b) => a.start_s - b.start_s);\n const mixInputs: Record<string, unknown> = {};\n sorted.forEach((s, k) => {\n mixInputs[`seg${k}`] = s.ref;\n });\n nodes.push({\n id: trackId,\n type: \"audio_timeline\",\n tags: { role: keepRenderedVoice ? \"voice\" : \"voice_track\", on_camera: true },\n inputs: mixInputs,\n params: {\n tracks: sameSpeakerTrackEntries(sorted),\n total_ms: totalMs,\n },\n });\n if (!keepRenderedVoice) {\n nodes.push({\n id: convId,\n type: \"audio_voice_convert\",\n tags: { role: \"voice\", on_camera: true },\n inputs: { audio: ref(trackId, \"audio\"), voice_ref: ref(voiceNode, \"voice_id\") },\n params: { model: FIXED_VOICE_CONVERT_MODEL, voice: \"{{voice_ref}}\" },\n });\n }\n // The merged track is already absolute-timed, so it sits at 0 on the final mix —\n // hard-capped to the timeline length so an oversized STS return (ElevenLabs\n // speech-to-speech is ASSUMED to preserve duration, but nothing verifies it)\n // truncates instead of pushing the mix past the picture into `-shortest`.\n tracks.push({ slot: convId, ref: ref(convId, \"audio\"), start_s: 0, duration_s: totalMs / 1000, kind: \"vo\" });\n }\n return tracks;\n}\n\n/**\n * One voice_select per distinct (canonical) speaker, cast from its description and reused.\n *\n * A speaker's casting is decided before anything is emitted: `isCast(speaker)` says whether\n * any node will read their voice. Every speaker keeps an id — the name the phrase and the\n * segments carry — but only a cast speaker gets the node. The one who does not is an\n * on-camera speaker whose rendered voice is kept (`keepRenderedVoice`) and who has no line\n * read over the picture: a casting there is a billed ElevenLabs search picking a voice for\n * a face that already has one, which nothing would ever reference.\n */\nexport function makeVoiceFactory(\n blueprint: VideoBlueprint,\n canonical: (s: string) => string,\n nodes: NodeDecl[],\n isCast: (speaker: string) => boolean,\n /**\n * The hard filters the caller knows: the voice's language (the English name `voice_select`\n * filters on) and the gender each description asks for. See `ScaffoldVideoOpts`.\n */\n filters: { language?: string; genders?: Readonly<Record<string, VoiceGender>> },\n /** A voice already chosen, so nothing is cast. See `ScaffoldVideoOpts.voiceId`. */\n pinnedVoiceId?: string,\n): (speaker: string) => string {\n const bySpeaker = new Map<string, string>();\n const describe = (speaker: string): string => {\n for (const scene of blueprint.scenes)\n for (const line of scene.dialogue ?? [])\n if (canonical(line.speaker ?? \"voiceover\") === speaker && line.voice_description) return line.voice_description;\n const cast = blueprint.global?.cast?.find((c) => c.id === speaker);\n return cast?.description ?? blueprint.global?.voiceover?.voice_description ?? `${speaker} voice`;\n };\n return (speaker: string): string => {\n const existing = bySpeaker.get(speaker);\n if (existing) return existing;\n const id = ids.voice(speaker, bySpeaker.size);\n bySpeaker.set(speaker, id);\n if (!isCast(speaker)) return id;\n const description = describe(speaker);\n // Gender and language as explicit params so the backend's hard-filter can refuse a\n // wrong-gender voice (never cast a male voice for a French woman). Both were decided\n // when the canvas was scaffolded; with neither, the description alone ranks the cast.\n const genders = filters.genders ?? {};\n const gender = Object.hasOwn(genders, description) ? genders[description] : undefined;\n const traits = {\n ...(gender ? { gender } : {}),\n ...(filters.language ? { language: filters.language } : {}),\n };\n // A pinned voice still gets a node, so every downstream `$ref:<node>.voice_id`\n // keeps working — it just resolves to the id that was chosen instead of casting a\n // new one. `voice_select` with an exact `voice_id` short-circuits its own ranking.\n nodes.push(\n pinnedVoiceId\n ? { id, type: \"voice_select\", params: { description, voice_id: pinnedVoiceId, ...traits } }\n : { id, type: \"voice_select\", params: { description, ...traits } },\n );\n return id;\n };\n}\n\n/**\n * A phrase nobody is shown saying: ONE ElevenLabs tts read over the picture (a voiceover\n * over the cutaways, or one speaker of a scene two people talk in). `phraseIndex` names it.\n */\nexport function emitPhraseTts(\n phrase: Phrase,\n voiceNode: string,\n phraseIndex: number,\n env: TimelineEnv,\n out: TimelineOut,\n): void {\n let id = ids.phraseRead(phraseIndex, phrase.speaker);\n while (out.readIds.has(id)) id = `${id}_x`;\n out.readIds.add(id);\n out.nodes.push({\n id,\n type: \"tts\",\n tags: { scene: phrase.firstScene, role: \"voice\", on_camera: false },\n inputs: { voice_ref: ref(voiceNode, \"voice_id\") },\n params: {\n model: FIXED_TTS_MODEL,\n text: phrase.text,\n voice: \"{{voice_ref}}\",\n // ISO 639-1 of the spoken track, when known — forces ElevenLabs to normalize\n // numbers/units in that language instead of defaulting to English.\n ...(env.ttsLanguageCode ? { language_code: env.ttsLanguageCode } : {}),\n },\n });\n // Spine-clock placement: the read starts where its first scene's picture sits.\n const spineStart = env.clock.map(phrase.firstScene, phrase.start_s);\n const spineEnd = spineStart + (phrase.end_s - phrase.start_s);\n out.voTracks.push({ slot: id, ref: ref(id, \"audio\"), start_s: spineStart, end_s: spineEnd, kind: \"vo\" });\n out.voSegments.push({\n slot: id,\n start_s: spineStart,\n end_s: spineEnd,\n scene: phrase.firstScene,\n speaker: phrase.speaker,\n onCamera: false,\n });\n}\n","import type { VideoBlueprint } from \"@baker/canvas-contract\";\nimport { isCtaRole } from \"../../lib/scene-role.ts\";\nimport { canvasDims } from \"../aspect.ts\";\nimport { colorPlateArgs } from \"../ffmpeg-args.ts\";\nimport { ids, ref, sceneTags } from \"../ids.ts\";\nimport type { SceneCtx } from \"../timeline-env.ts\";\n\n// The prose cue for a CTA/end beat that is a SOLID brand/logo card, for a deconstructed\n// scene: a producer that wrote the beat as a brand card says so in `route_hint` instead.\nconst BRAND_CARD_RE =\n /\\b(?:solid|plain|flat|brand|logo|wordmark|end[- ]?card|cta card|title card|colou?r background|background colou?r)\\b/i;\n\n/**\n * A CTA/end beat that is a SOLID brand/logo card (not a photographic scene). Must NOT be\n * image_generate'd: a generative model garbles the wordmark and URL (\"Barkbu\",\n * \"barkaui.com\") and can letterbox the plate (CCA-05/12). Reproduced as a clean solid\n * colour plate; the real logo/URL/CTA ride the overlay layer's brand card.\n *\n * A scene whose producer marked it `route_hint: \"brand_card\"` (`scaffold-ad`'s closing\n * beat) is one. A deconstructed scene carries no hint and is read from its prose: the\n * closing (or declared CTA) beat, with a solid/brand/logo cue and no on-camera cast (a\n * presenter CTA stays a talking head).\n */\nexport function sceneIsBrandCard(ctx: SceneCtx): boolean {\n const { scene } = ctx;\n if (scene.route_hint) return scene.route_hint === \"brand_card\";\n if (!(isCtaRole(scene.narrative_role) || ctx.isLast)) return false;\n const hasCast = ctx.present.some((s) => {\n const t = s.type.toLowerCase();\n return t === \"person\" || t === \"animal\";\n });\n if (hasCast) return false;\n const hay = `${scene.summary ?? \"\"} ${scene.start_frame_prompt ?? \"\"} ${scene.end_frame_prompt ?? \"\"}`;\n return BRAND_CARD_RE.test(hay);\n}\n\nconst HEX6_RE = /^#?[0-9a-fA-F]{6}$/;\n/** The brand-card plate colour: the blueprint's dominant palette hex, else clean black\n * (the overlay CTA card carries the brand look, so a neutral plate is safe). */\nfunction brandPlateColor(blueprint: VideoBlueprint): string {\n const hex = blueprint.global?.style?.palette\n ?.map((p) => p.hex)\n .find((h): h is string => typeof h === \"string\" && HEX6_RE.test(h));\n return hex ? `0x${hex.replace(/^#/, \"\").toUpperCase()}` : \"0x000000\";\n}\n\n/** The `brand_card` route: a clean solid colour plate at the exact canvas size (never a generated logo). */\nexport function emitBrandCardScene(ctx: SceneCtx): void {\n const { env, index, lengths } = ctx;\n const plateId = ids.clip(index);\n ctx.out.nodes.push({\n id: plateId,\n type: \"ffmpeg\",\n tags: sceneTags(index, \"brand_card\"),\n inputs: {},\n params: {\n args: colorPlateArgs(lengths.trimTarget, canvasDims(env.outAr), brandPlateColor(env.blueprint)),\n outputs: { video: { kind: \"video\", ext: \"mp4\" } },\n },\n });\n ctx.out.clips.push({ ref: ref(plateId, \"video\"), scene_s: lengths.dur, out: lengths.out });\n ctx.out.routes.set(index, \"brand_card\");\n}\n","// Ad-native SHOOT-MODE grammar — the single source of truth for the \"look\" each\n// scene is captured in. A video ad is rarely one aesthetic end-to-end: a creator\n// selfie hook, a candid b-roll demo, and a clean pack-shot are filmed three\n// different ways. The scaffold used to bake ONE look (phone UGC) into every\n// frame; this picks the right capture block per scene instead.\n//\n// The reference doc `references/shoot-modes.md` in the creative-canvas-ads skill\n// describes these modes for the human; THIS file is the canonical definition the\n// scaffold actually emits — keep the two in sync conceptually, but never paste the\n// prose into both places (this is the source).\n//\n// Doctrine: UGC is still the default and the auto-derived modes are UGC/product\n// only (selfie, b-roll, studio-product). The polished lanes (lifestyle-cinematic,\n// screen/UI) exist but are opt-in — the deconstruct/agent must name them on a\n// scene, because a cinematic grade applied by accident is exactly the \"produced,\n// not filmed\" tell that kills UGC performance on feed.\n\nexport type ShootMode = \"ugc_selfie\" | \"ugc_broll\" | \"studio_product\" | \"lifestyle_cinematic\" | \"screen_ui\";\n\nconst SHOOT_MODES: readonly ShootMode[] = [\n \"ugc_selfie\",\n \"ugc_broll\",\n \"studio_product\",\n \"lifestyle_cinematic\",\n \"screen_ui\",\n] as const;\n\n/** Talking heads default here; the safest, highest-converting feed look. */\nexport const DEFAULT_SHOOT_MODE: ShootMode = \"ugc_selfie\";\n\ntype ShootModeSpec = {\n label: string;\n /** Whether shallow depth of field reads as native for this mode. UGC = flat. */\n allowsDoF: boolean;\n /** The frame capture block — the aesthetic + DoF rule injected into image_generate. */\n capture: string;\n /** The Seedance camera/motion default when the scene names no explicit move. */\n motion: string;\n /** Default diegetic-audio descriptor when the scene carries no `ambient` hint. */\n diegetic: string;\n};\n\nconst SHOOT_MODE_SPECS: Readonly<Record<ShootMode, ShootModeSpec>> = {\n ugc_selfie: {\n label: \"UGC selfie / talking-head\",\n allowsDoF: false,\n capture: [\n \"CAPTURE — AUTHENTIC PHONE UGC (front camera):\",\n \"Shot on a modern phone front camera — natural lens, real skin texture and pores,\",\n \"catchlights, mixed indoor white balance, faint sensor grain, slight handheld imperfection.\",\n \"NO shallow depth of field or background blur — native phone footage is flat front-to-back;\",\n \"blur reads as 'produced', not filmed-on-a-phone. Keep the whole frame in focus.\",\n ].join(\"\\n\"),\n motion:\n \"Lock the camera at arm's-length selfie distance; only natural handheld micro-movement. Move the camera only if a move is named above.\",\n diegetic: \"a quiet room tone with soft fabric and breath under the speaker's own voice\",\n },\n ugc_broll: {\n label: \"UGC b-roll / handheld\",\n allowsDoF: false,\n capture: [\n \"CAPTURE — AUTHENTIC PHONE UGC (rear camera, candid):\",\n \"Shot on a modern phone rear camera, handheld and candid — natural lens, real materials and\",\n \"textures, real hands in frame where natural, mixed natural white balance, faint sensor grain.\",\n \"NO shallow depth of field or background blur — native phone footage is flat front-to-back;\",\n \"keep the whole frame in focus.\",\n ].join(\"\\n\"),\n motion:\n \"Handheld, candid framing; keep any move small and motivated. Move the camera only if a move is named above.\",\n diegetic: \"the real ambient of the setting — handling sounds, footsteps, and room or outdoor tone\",\n },\n studio_product: {\n label: \"Studio / product (pack shot)\",\n allowsDoF: true,\n capture: [\n \"CAPTURE — CONTROLLED PRODUCT / STUDIO:\",\n \"Photographed on a controlled set — a clean seamless or one styled surface, a soft key with\",\n \"gentle fill and a subtle rim, true-to-life color. Shallow depth of field IS allowed to isolate\",\n \"the hero, with crisp specular highlights on the product's real materials.\",\n \"Still a real photograph, not CGI — no plastic or waxy surfaces, no over-render; real material\",\n \"texture and weight.\",\n ].join(\"\\n\"),\n motion: \"Lock off, or a slow motivated push-in / settle onto the product; otherwise hold.\",\n diegetic: \"minimal — soft product-handling sounds over a quiet room tone\",\n },\n lifestyle_cinematic: {\n label: \"Lifestyle / cinematic\",\n allowsDoF: true,\n capture: [\n \"CAPTURE — LIFESTYLE / CINEMATIC:\",\n \"A real camera in a real location — natural motivated light, true color, a gentle filmic grade,\",\n \"fine grain. A shallow depth of field is allowed when motivated by the moment.\",\n \"Photographic, not rendered — real skin and material texture, no airbrushing, no glossy 3D look.\",\n ].join(\"\\n\"),\n motion: \"A slow, motivated camera move (gentle push-in, drift, or settle) is allowed; otherwise hold.\",\n diegetic: \"the location's natural ambience — wind, traffic, water, or room tone as the setting implies\",\n },\n screen_ui: {\n label: \"Screen / UI / demo\",\n allowsDoF: true,\n capture: [\n \"CAPTURE — SCREEN / UI CAPTURE:\",\n \"A clean screen or app capture — crisp pixels, true on-screen color, optionally framed inside a\",\n \"real device held in a real hand. No human-skin realism is needed; the screen content is the subject.\",\n \"Do not bake invented UI copy into the plate beyond what the reference shows — editable text lives\",\n \"on the overlay layer.\",\n ].join(\"\\n\"),\n motion: \"Hold on the screen; allow a slow push-in or a UI scroll only if a move is named above.\",\n diegetic: \"soft UI taps and device handling over a quiet room tone\",\n },\n};\n\nexport function isShootMode(value: unknown): value is ShootMode {\n return typeof value === \"string\" && (SHOOT_MODES as readonly string[]).includes(value);\n}\n\n/** The capture block (aesthetic + DoF rule) for a frame in this mode. */\nexport function captureBlockFor(mode: ShootMode): string {\n return SHOOT_MODE_SPECS[mode].capture;\n}\n\n/** The default camera/motion line for a Seedance clip in this mode. */\nexport function seedanceMotionFor(mode: ShootMode): string {\n return SHOOT_MODE_SPECS[mode].motion;\n}\n\n/** The default diegetic-audio descriptor for a scene in this mode. */\nexport function diegeticFor(mode: ShootMode): string {\n return SHOOT_MODE_SPECS[mode].diegetic;\n}\n\n/**\n * Pick the shoot mode for a scene. An explicit `scene.shoot_mode` the deconstruct\n * or a human set always wins. Otherwise we auto-derive — and on purpose only ever\n * choose a UGC/product mode: talking → selfie, a product-only beat → pack shot,\n * everything else → candid b-roll. The cinematic and screen lanes are never\n * auto-selected; they must be named, so the UGC default holds unless asked.\n */\nexport function deriveShootMode(opts: {\n explicit?: unknown;\n talking: boolean;\n hasPerson: boolean;\n hasProduct: boolean;\n}): ShootMode {\n if (isShootMode(opts.explicit)) return opts.explicit;\n if (opts.talking) return \"ugc_selfie\";\n if (opts.hasProduct && !opts.hasPerson) return \"studio_product\";\n return \"ugc_broll\";\n}\n","import type { VideoScene } from \"@baker/canvas-contract\";\nimport { imageProfileFor } from \"../../../lib/prompt-profiles.ts\";\nimport { captureBlockFor, type ShootMode } from \"../../lib/shoot-modes.ts\";\nimport { type ElementSlot, roleForSlot } from \"../elements.ts\";\nimport type { Edge } from \"../ids.ts\";\n\n/** Ends the FRAME DESCRIPTION a frame gets when its blueprint gave it none: a prompt to write one, not one. */\nexport const FRAME_PLACEHOLDER_NOTE = \"(Edit this line to change ONLY this frame.)\";\n\n/**\n * Build the static-ad-grade prompt for one boundary frame.\n *\n * The prompt is SELF-CONTAINED and is the per-frame editable surface: the FRAME\n * DESCRIPTION is inlined verbatim from the blueprint's `{edge}_frame_prompt`, so\n * editing this one node's `params.prompt` changes ONLY this frame — no other\n * frame and no shared file is touched. The blueprint (`{{target_blueprint}}`) is\n * kept ONLY as a demoted, shared style reference (global cast identity, palette,\n * typography mood, aspect ratio) — never as the source of this frame's content.\n */\nexport function buildFramePrompt(frame: {\n edge: Edge;\n sceneIndex: number;\n /** The frame's own description; a placeholder asks for one when the blueprint has none. */\n framePrompt: string | undefined;\n /** The references the frame grounds on, in the order they are wired. */\n present: ElementSlot[];\n /** Whether the source frame rides last as a composition-only anchor. */\n hasAnchor: boolean;\n mode: ShootMode;\n sceneStyle: SceneStyle | undefined;\n // The frame's image model — picks the model's PROMPT profile: gpt-image weights\n // early text as composition, so its exclusions go LAST and it needs an explicit\n // \"photorealistic\" cue; Gemini reads exclusions inline; Recraft renders vector art.\n imageModel: string;\n}): string {\n const { edge, sceneIndex, framePrompt, present, hasAnchor, mode, sceneStyle } = frame;\n const profile = imageProfileFor(frame.imageModel);\n const exclusionsLast = profile?.constraintPlacement === \"last\";\n // Default photoreal ON for unprofiled models (frames are photographic unless a\n // vector model like Recraft opts out).\n const photoreal = profile ? profile.photorealCue : true;\n const EDGE = edge.toUpperCase();\n const legend = [\n ...present.map((s) => `- ${s.label} — ${roleForSlot(s)}`),\n ...(hasAnchor\n ? [\n \"- ORIGINAL_FRAME — use ONLY for composition: framing, camera angle, shot size, subject placement, pose, and proportions. IGNORE its text, logo, brand name, colors, AND the identity of every person/animal/object in it — those come from the labeled reference images above, never from this frame. It is a DIFFERENT brand's footage with DIFFERENT actors, here ONLY to anchor where things sit and how the shot is framed (e.g. a profile/side angle stays a profile/side angle), never who they are or what palette to use.\",\n ]\n : []),\n ].join(\"\\n\");\n const description =\n framePrompt?.trim() ||\n `the ${edge} frame of scene ${sceneIndex + 1} — describe the full composition, subjects, setting, action, lighting, and palette here. ${FRAME_PLACEHOLDER_NOTE}`;\n // The hook frame (scene 1's opening) carries the ad sound-off: the feed plays\n // muted, so this single frame + its overlay must read in ~1 second.\n const isHookFrame = sceneIndex === 0 && edge === \"start\";\n // The clean-plate exclusions. Their PLACEMENT is model-specific: gpt-image treats\n // early exclusions as compositional guidance, so for it we hoist them to the very\n // end; other models read them fine up top.\n const cleanPlateBlock = [\n \"CRITICAL — RENDER A CLEAN PLATE WITH ZERO TEXT OR GRAPHIC OVERLAYS:\",\n \"This frame is a background plate. Every overlay element is composited afterwards as a\",\n \"separate hyperframe HTML overlay layer — NOT painted into this image. Render NONE of:\",\n \"words, captions, subtitles, headlines, lower-third bars, news tickers/crawls, chyrons,\",\n \"station bugs, watermarks, numbers, prices; and NONE of the graphic overlays layered on\",\n \"the picture either — icons, stickers, emojis, badges, rating/trust seals, progress bars,\",\n \"UI chrome/buttons, and arrows. No legible text anywhere, not even in the background, on a\",\n \"desk, on screens, or as part of a 'broadcast look'. If a reference image (a logo, a desk,\",\n \"a studio) contains any text or graphics, DO NOT reproduce them — render the subject/scene\",\n \"only, leaving the regions where overlays will sit clean. Imperfect/garbled letterforms or\",\n \"stray icons are the worst outcome; leave those areas blank.\",\n \"A SCREEN/UI surface — an app, website, chat, dashboard, or phone display — is NEVER\",\n \"rendered here: leave any phone/screen OFF or blank-screened. The real interface is\",\n \"composited later as a screenshot or a brand HTML block, never AI-generated.\",\n ];\n return [\n `Render the ${EDGE} frame of scene ${sceneIndex + 1} as a single${photoreal ? \", photorealistic\" : \"\"} still image. This prompt is self-contained and edit-per-frame: change the FRAME DESCRIPTION below to alter ONLY this frame.`,\n \"\",\n // gpt-image (exclusionsLast): exclusions are hoisted to the tail (below); here we\n // lead with the positive brief. All other models keep the exclusions up front.\n ...(exclusionsLast ? [] : [...cleanPlateBlock, \"\"]),\n \"FRAMING — ONE UNCUT FRAME:\",\n \"Render ONE single uncut photographic frame: NO split screen, NO panels, NO dividing line,\",\n \"NO collage, NO before/after. Avoid the AI look — no waxy/plastic skin, no airbrushing, no\",\n \"over-smoothing, no over-saturation, no glossy 3D render. Every descriptor needs a technical\",\n 'anchor (a named lens / focal length / color grade) — no empty adjectives like \"cinematic\",',\n '\"beautiful\", \"high quality\"; they waste tokens and don\\'t move the model.',\n \"\",\n // The capture aesthetic + depth-of-field rule are SHOOT-MODE specific: a UGC\n // selfie is flat phone footage; a pack shot is a controlled studio frame. Only\n // this block varies by mode — the clean-plate and framing rules above are universal.\n captureBlockFor(mode),\n \"\",\n // Moderation-safe phrasing — Seedance routes around the real-person filter but\n // prompts still hit provider moderation; age-blind, role-based descriptions trip\n // it far less. (The client's own brand assets come from the references, not here.)\n \"Describe any person by role, wardrobe, and build — never by name and never by age\",\n \"(no child/kid/teen/young/elderly); do not invent brand logos or marks.\",\n ...(isHookFrame\n ? [\n \"\",\n \"HOOK FRAME (scene 1 opens the ad): the feed plays muted, so this frame must read\",\n \"INSTANTLY SOUND-OFF — one clear subject, legible at a glance in ~1 second, no clutter.\",\n ]\n : []),\n \"\",\n \"REFERENCE IMAGES (in the order provided):\",\n legend,\n \"\",\n // RECAST is the whole point of a transform: the dropped el_* images define who/\n // what is on screen, NOT the source footage and NOT the prose. Without this, the\n // model reproduces the original ad's people (a proven failure mode).\n ...(present.length > 0\n ? [\n \"IDENTITY & AESTHETIC — RECAST (this is a transform, not a copy):\",\n \"Identity comes from the reference image, never from the source footage or this prose. Render every\",\n \"person, animal, product, and set to MATCH its labeled reference image above — that image is the ONLY\",\n \"source of their identity, wardrobe, styling, and look. This is a complete recast: do NOT reproduce,\",\n \"trace, or resemble any individual, animal, product, or set from the source ad. Where the FRAME\",\n \"DESCRIPTION below names an appearance detail (hair, outfit, color, age, breed, brand of an object),\",\n \"IGNORE that wording — the reference image is the truth; use the description ONLY for pose, expression,\",\n \"action, framing, lighting, and palette.\",\n \"\",\n ]\n : [\n \"Identity comes from the reference image, never from prose — render the subject to MATCH it and\",\n \"describe only pose, expression, action, framing, and lighting in the FRAME DESCRIPTION below.\",\n \"\",\n ]),\n // Per-scene style deltas (a mid-video wardrobe/set change, a scene-specific\n // ambience) never reach the SHARED spec — it is global-only by design — so they\n // are baked HERE, on this scene's own frames. Inserted BEFORE the FRAME\n // DESCRIPTION marker so the validator's description slicing stays intact.\n ...(sceneStyle?.setting || sceneStyle?.ambient\n ? [\n \"SCENE STYLE (this scene's own setting/ambience — where it differs from the SHARED AD SPEC, this wins):\",\n [sceneStyle.setting, sceneStyle.ambient].filter(Boolean).join(\" — \"),\n \"\",\n ]\n : []),\n \"FRAME DESCRIPTION (this frame's editable prompt):\",\n description,\n \"\",\n \"Render exactly what the FRAME DESCRIPTION and the SHARED AD SPEC specify — this is the authoritative ad: its cast identity (via the reference images), palette, brand, and intent are law. Keep every recurring element identical to its reference image across all frames. Again: clean plate only — no rendered text, and no icons/stickers/emojis/badges (those live on the overlay layer).\",\n \"\",\n \"SHARED AD SPEC (authoritative — the ad blueprint this frame belongs to; align cast/palette/brand/type with it):\",\n \"{{target_blueprint}}\",\n // gpt-image weights early text as compositional guidance and late text as pure\n // exclusion — so for it the clean-plate rules land HERE, after all positive content.\n ...(exclusionsLast ? [\"\", ...cleanPlateBlock] : []),\n ].join(\"\\n\");\n}\n\n// A scene's own style deltas (setting/wardrobe/ambience) baked into ITS frames only —\n// the shared spec is global by design, so per-scene changes must ride the frame prompt.\nexport type SceneStyle = { setting?: string; ambient?: string };\n\n/** The frame-prompt style block for a scene: its own setting + ambience, when present. */\nexport function sceneStyleOf(scene: VideoScene): SceneStyle | undefined {\n const setting = scene.scene_setting?.trim();\n const ambient = scene.ambient?.trim();\n return setting || ambient ? { ...(setting ? { setting } : {}), ...(ambient ? { ambient } : {}) } : undefined;\n}\n","import { nearestSupportedAspectRatio } from \"@baker/canvas-contract\";\nimport { imageRenderParams } from \"../lib/image-params.ts\";\nimport { type ElementSlot, orderSlotsByIdentity } from \"./elements.ts\";\nimport { type Edge, ids, ref, sceneTags } from \"./ids.ts\";\nimport { buildFramePrompt, sceneStyleOf } from \"./prompts/frame.ts\";\nimport type { SceneCtx } from \"./timeline-env.ts\";\n\n// The tier keyframes ask for. Video models downscale to their own output anyway,\n// but a sharp source frame is what keeps the clip from looking softer than the\n// storyboard; snapped down per model by `imageRenderParams`.\nconst KEYFRAME_IMAGE_SIZE = \"2K\";\n// The step asked for on every frame a clip is built from; snapped down per model by\n// `imageRenderParams`, exactly as the size above is.\nconst FRAME_QUALITY = \"xhigh\";\n\n/** One keyframe to build: its edge, the source frame it reproduces, and who is in it. */\nexport type FrameSource = {\n edge: Edge;\n /** The real extracted frame, when the deconstruct kept one. */\n url: string | undefined;\n /** The frame's own description (its FRAME DESCRIPTION). */\n prompt: string | undefined;\n /** The recurring-element slots present in the frame. */\n present: ElementSlot[];\n /** The region of a composited scene the frame belongs to; absent for a full frame. */\n region?: number;\n};\n\n/**\n * Ingest a source-frame URL exactly once per canvas (a source-frame URL → its ingest ref in\n * `env.ingestCache`) and return its asset ref. Two scenes that share a frame URL reuse the\n * same ingest node.\n */\nfunction ingestFrameRef(ctx: SceneCtx, frame: FrameSource, url: string): string {\n const cached = ctx.env.ingestCache.get(url);\n if (cached) return cached;\n const refId = ids.sourceFrame(ctx.index, frame.edge, frame.region);\n ctx.out.nodes.push({ id: refId, type: \"ingest\", params: { source: \"url\", url, expect: \"image\" } });\n const assetRef = ref(refId, \"asset\");\n ctx.env.ingestCache.set(url, assetRef);\n return assetRef;\n}\n\n/**\n * Emit the node(s) for one clip's SINGLE keyframe and return the ref to wire into\n * video_generate's `first_frame`. Ingest the real frame (when present), then either\n * reuse it directly (faithful mode) or regenerate it static-ad-style — the baked\n * blueprint as target_blueprint, the shared recurring-element images + the real\n * frame as references, and a per-element legend.\n *\n * Every keyframe grounds ONLY on its OWN extracted frame + el_* identity slots — no\n * reference to any OTHER generated frame. So every image_generate is independent and\n * the whole canvas's frames render in PARALLEL (no cascade), and Seedance animates\n * each clip forward from its single first_frame.\n */\nexport function buildFrameRef(ctx: SceneCtx, frame: FrameSource): string {\n const { env } = ctx;\n const { url, present } = frame;\n // Reuse mode: wire the real source frame straight in (faithful, no regen).\n if (env.reuse && url) return ingestFrameRef(ctx, frame, url);\n\n // Generate mode — built EXACTLY like a static-ad frame: the `el_*` recast images own\n // identity/aesthetic (first). The ORIGINAL extracted frame may be kept LAST as a\n // pure-composition anchor (framing / camera angle / shot size / pose / proportions).\n //\n // Keeping that anchor reproduces the source's framing — but on ANY identity-bearing\n // frame (a person/pet present) it LEAKS the SOURCE identity: the model copies the\n // competitor's face from a strong close-up even when the cast is sheet-backed\n // (CCA-01 — the source actress rendered on the hook/CTA, then morphed mid-clip\n // between two mismatched keyframes). A multi-view sheet was NOT a strong enough lock.\n // So drop the raw anchor whenever a person/animal is present and ground purely on the\n // recast `el_*` sheets — framing is carried by the frame prompt's pose/angle/shot-size\n // instead. Keep the anchor only for NON-identity scenes (product / scenery / b-roll),\n // where it can't leak a face and genuinely helps the composition. The frame prompt's\n // ORIGINAL_FRAME legend still forbids taking identity/text/palette from it.\n //\n // The per-frame cast is not enough on its own: presence comes from the element\n // selection, which is told to leave a scene out when in doubt, so a frame of the\n // source actor can arrive here with no person slot. In an ad that shows people at all,\n // no source frame is a reference (BAKER-652).\n const castSlots = present.filter((s) => {\n const t = s.type.toLowerCase();\n return t === \"person\" || t === \"animal\";\n });\n const originalRef =\n url && castSlots.length === 0 && !env.sourceShowsPeople ? ingestFrameRef(ctx, frame, url) : undefined;\n // Identity-critical assets lead (the presenter's face gets the first-reference richness\n // gpt-image reserves); the pure-composition anchor always stays last. The legend order\n // inside the prompt is built from this same ordered list, so they can't disagree.\n const presentOrdered = orderSlotsByIdentity(present);\n const reference = [...presentOrdered.map((s) => s.ref), ...(originalRef ? [originalRef] : [])];\n const imageModel = env.opts.imageModel;\n const genParams: Record<string, unknown> = {\n model: imageModel,\n // Size tier, quality step and the model's profile defaults, each snapped to what the\n // chosen model offers (gpt-image has no size knob; Nano Banana 2 Lite renders 1K only).\n // We do NOT send input_fidelity, which gpt-image forces high automatically. The frame IS\n // the clip's first moment, and the video model animates whatever it is handed: a soft\n // frame is a soft clip for its whole length, so it asks for the best step there is.\n ...imageRenderParams(imageModel, { size: KEYFRAME_IMAGE_SIZE, quality: FRAME_QUALITY }),\n prompt: buildFramePrompt({\n edge: frame.edge,\n sceneIndex: ctx.index,\n framePrompt: frame.prompt,\n present: presentOrdered,\n hasAnchor: originalRef !== undefined,\n mode: ctx.mode,\n sceneStyle: sceneStyleOf(ctx.scene),\n imageModel,\n }),\n // The keyframes condition the clip, so they are generated at the clip's ratio.\n aspect_ratio: nearestSupportedAspectRatio(\"image_generate\", imageModel, env.genAr),\n };\n const genId = ids.frame(ctx.index, frame.edge, frame.region);\n ctx.out.nodes.push({\n id: genId,\n type: \"image_generate\",\n tags: sceneTags(ctx.index, \"frame\", frame.region),\n // `params.prompt` is this frame's authoritative, edit-per-frame description.\n // `target_blueprint` is the SLIM shared ad spec (global cast identity, palette, brand,\n // type — no per-scene content) the frame must stay consistent with; editing one frame\n // never touches another, and no image inlines the whole film to render one frame.\n inputs: { target_blueprint: ref(ids.promptStyle, \"asset\"), ...(reference.length > 0 ? { reference } : {}) },\n params: genParams,\n });\n return ref(genId, \"images#0\");\n}\n","import { MODEL_REGISTRY } from \"@baker/canvas-contract\";\n\n// Seedance (and Veo) default to a LOW output resolution when `resolution` is unset,\n// which downscales the 2K `image_generate` keyframes and is the top cause of the\n// \"the video looks way worse than the frames\" complaint. Pin the highest tier both\n// the default video model (Seedance: 480/720/1080p) and the cheap test model (Veo:\n// 720/1080p) share, so the clip preserves the keyframe's sharpness. Override per run\n// via `--resolution`. Only applied to models whose registry schema accepts it.\nconst DEFAULT_VIDEO_RESOLUTION = \"1080p\";\n// Derived from the registry (not hardcoded) so a future video model that declares a\n// `resolution` param is pinned automatically — a hardcoded list would silently leave\n// it on the model's low default, reproducing the regression this pin fixes.\nconst VIDEO_MODELS_WITH_RESOLUTION = new Set(\n Object.entries(MODEL_REGISTRY.video_generate)\n .filter(([, spec]) => \"resolution\" in spec.params)\n .map(([id]) => id),\n);\n/** The `resolution` param to merge into a video_generate node, or {} when the model has none. */\nexport function videoResolutionParam(videoModel: string, resolution: string | undefined): { resolution?: string } {\n if (!VIDEO_MODELS_WITH_RESOLUTION.has(videoModel)) return {};\n return { resolution: resolution ?? DEFAULT_VIDEO_RESOLUTION };\n}\n\n// `duration` and `generate_audio` are gated the same registry-derived way as\n// resolution: emitting a param the model has no schema slot for is a hard\n// `unknown_param` failure and the whole canvas refuses to run.\n//\n// Their status differs today. `generate_audio` genuinely varies — Gemini Omni has\n// no toggle (it always renders sound), so the helper really does return `{}`.\n// EVERY curated model declares `duration`, so `videoDurationParam` currently always\n// emits; it is kept for symmetry with its siblings so a future fixed-length model\n// is handled here rather than silently breaking every canvas that picks it.\nconst VIDEO_MODELS_WITH_DURATION = new Set(\n Object.entries(MODEL_REGISTRY.video_generate)\n .filter(([, spec]) => \"duration\" in spec.params)\n .map(([id]) => id),\n);\nconst VIDEO_MODELS_WITH_AUDIO_TOGGLE = new Set(\n Object.entries(MODEL_REGISTRY.video_generate)\n .filter(([, spec]) => \"generate_audio\" in spec.params)\n .map(([id]) => id),\n);\n\n/** The `duration` param for a video_generate node, or {} if the model declares none. */\nexport function videoDurationParam(videoModel: string, duration: number): { duration?: number } {\n if (!VIDEO_MODELS_WITH_DURATION.has(videoModel)) return {};\n return { duration };\n}\n\n/**\n * The `generate_audio` param, or {} when the model has no toggle. A model without\n * one (Omni) always produces audio, so a caller asking for a silent clip gets\n * sound anyway — the spine mutes it downstream rather than the model omitting it.\n */\nexport function videoAudioParam(videoModel: string, generateAudio: boolean): { generate_audio?: boolean } {\n if (!VIDEO_MODELS_WITH_AUDIO_TOGGLE.has(videoModel)) return {};\n return { generate_audio: generateAudio };\n}\n","import { BlueprintFloatingElementSchema, type CompositionRegion, type VideoScene } from \"@baker/canvas-contract\";\nimport { z } from \"zod\";\nimport type { ClipPromptProfile } from \"../../../lib/prompt-profiles.ts\";\nimport { diegeticFor, type ShootMode, seedanceMotionFor } from \"../../lib/shoot-modes.ts\";\nimport { localizeNumeralsForNative } from \"../dialogue/numbers.ts\";\nimport type { ElementSlot } from \"../elements.ts\";\nimport { compositePlateRegion, UI_SURFACE_RE } from \"../routing.ts\";\nimport type { TimelineEnv } from \"../timeline-env.ts\";\n\n/**\n * The raw ASR transcript for a clip prompt — silent clips only. On a NATIVE clip the\n * Dialogue line is authoritative; echoing the transcript there doubles the chance\n * Seedance vocalizes recognition noise (mis-heard brand names, stutter fragments).\n */\nfunction silentClipTranscript(scene: VideoScene, native: boolean): string {\n if (native) return \"\";\n return (scene.transcript_slice ?? [])\n .map((w) => w.text?.trim())\n .filter(Boolean)\n .join(\" \")\n .trim();\n}\n\n/**\n * The diegetic audio line for a Seedance clip, or null when the clip is silent.\n * Native sound is generated, so when audio is on we name the diegetic ambience the\n * clip should produce and forbid music outright — the music bed is a separate,\n * ducked track and a Seedance-invented soundtrack would fight it. Ambient defaults\n * to the shoot mode's ambience when the scene names none.\n */\nfunction seedanceAudioLine(scene: VideoScene, mode: ShootMode, audio: boolean, nativeLine?: string): string | null {\n const ambient = scene.ambient?.trim() || diegeticFor(mode);\n if (nativeLine) {\n return `Audio: diegetic only — the speaker's own voice over ${ambient}; no music, no song, no soundtrack (the music bed is a separate track).`;\n }\n if (audio) {\n return `Audio: diegetic ambient only — ${ambient}; no spoken dialogue, no music, no song, no soundtrack (voice and music are separate tracks).`;\n }\n return null;\n}\n\n// Words too generic to identify a floating element (they'd nuke innocent sentences).\nconst FLOAT_STOP_WORDS = new Set([\n \"with\",\n \"across\",\n \"around\",\n \"above\",\n \"below\",\n \"over\",\n \"under\",\n \"screen\",\n \"frame\",\n \"scene\",\n \"video\",\n \"element\",\n \"elements\",\n \"graphic\",\n \"overlay\",\n \"animated\",\n \"floating\",\n \"small\",\n \"large\",\n \"white\",\n \"black\",\n \"color\",\n \"colored\",\n]);\n\n/** Distinctive tokens of a floating element's description (for sentence matching). */\nfunction floatTokens(desc: string): string[] {\n return desc\n .toLowerCase()\n .split(/[^a-z0-9]+/)\n .filter((w) => w.length >= 4 && !FLOAT_STOP_WORDS.has(w));\n}\n\n/**\n * Drop brief sentences that narrate an overlay-routed floating element (FX like\n * floating hearts, badges, torn-paper wipes). The element rides the hyperframes\n * overlay layer as a REAL asset — narrating it in the motion brief makes Seedance\n * bake a second, uneditable copy underneath (the double-phone defect class, for FX).\n * A sentence is dropped when it shares ≥2 distinctive tokens with one float.\n */\nexport function scrubFloatSentences(text: string, floatDescs: string[]): string {\n if (floatDescs.length === 0 || !text) return text;\n const tokenSets = floatDescs.map((d) => new Set(floatTokens(d)));\n const kept = text\n .split(/(?<=[.!?])\\s+/)\n .filter((sentence) => {\n const words = new Set(floatTokens(sentence));\n return !tokenSets.some((ts) => {\n let hits = 0;\n for (const w of words) if (ts.has(w)) hits++;\n return hits >= 2;\n });\n })\n .join(\" \")\n .trim();\n return kept;\n}\n\n/** The scene's floating-element descriptions (all ride the overlay layer). */\nfunction sceneFloatDescs(scene: VideoScene): string[] {\n const floats = z.array(BlueprintFloatingElementSchema).safeParse(scene.floating_elements ?? []);\n if (!floats.success) return [];\n return floats.data.map((f) => f.description?.trim() ?? \"\").filter(Boolean);\n}\n\n/** The brief's visual lines: summary, action, camera, motion — plate-scoped when set. */\nfunction visualBriefParts(scene: VideoScene, sceneIndex: number, plate: CompositionRegion | null): string[] {\n const parts: string[] = [];\n const floats = sceneFloatDescs(scene);\n const scrub = (s: string | undefined): string | undefined => {\n if (!s) return s;\n const out = scrubFloatSentences(s, floats);\n return out || undefined;\n };\n const summary = scrub((plate ? plate.summary : scene.summary)?.trim());\n parts.push(summary ? `Scene ${sceneIndex + 1}: ${summary}` : `Scene ${sceneIndex + 1}`);\n const action = !plate ? scrub(scene.action_detail) : undefined;\n if (action) parts.push(`Action: ${action}`);\n const cm = scene.camera_motion;\n if (cm) {\n const camera = [cm.movement, cm.detail].filter(Boolean).join(\" — \");\n if (camera) parts.push(`Camera: ${camera}`);\n }\n const motion = scrub(plate ? plate.motion_prompt : scene.motion_prompt);\n if (motion) parts.push(`Motion: ${motion}`);\n // A UI-flavored float (\"mobile screen recording card…\") is covered by the blanket\n // no-screen directive — quoting its description verbatim would prime the generator\n // with the exact words the plate scrub removed. Only non-UI FX floats are named.\n const uiFloats = floats.filter((d) => UI_SURFACE_RE.test(d));\n const fxFloats = floats.filter((d) => !UI_SURFACE_RE.test(d));\n if (plate || uiFloats.length > 0) {\n parts.push(\n \"NEVER render any screen, phone, app, website, or UI element — this clip is the clean background plate; the screen surface is composited on top later as a separate real layer.\",\n );\n }\n if (fxFloats.length > 0) {\n // Categorical on purpose: quoting the element's description verbatim (\"pink\n // hearts…\") primes the generator with exactly the words the scrub removed.\n parts.push(\n \"NEVER render floating decorative elements — no hearts, sparkles, confetti, badges, stickers, icons, or particle effects. They are composited later as a separate real overlay layer; the picture stays clean of them.\",\n );\n }\n return parts;\n}\n\n/**\n * The spoken-text lines of a clip prompt: the native dialogue directive (per-model\n * syntax), or the silent clip's spoken context + transcript. Numbers in anything the\n * model can vocalize are spelled into the spoken language by `loc` — a bare \"6930\"\n * reintroduces the English digits the localization just fixed.\n */\nfunction spokenTextParts(\n scene: VideoScene,\n nativeLine: string | undefined,\n loc: (s: string) => string,\n profile: ClipPromptProfile,\n // Which variety of the language the speech is in. The model picks one otherwise, and\n // picks the commonest: ads set in Spain came back in Latin American Spanish, which the\n // pinned peninsular voice could not undo because the re-voicing keeps the delivery.\n spokenAccent?: string,\n): string[] {\n const parts: string[] = [];\n if (nativeLine) {\n // Native talking head: the model generates the speech + matching lip-sync. Spell any\n // numbers into the target language so they aren't read in English. Delivery is carried\n // in prose around the quoted line (both Seedance 2.0 and Veo — brackets misfire).\n parts.push(profile.dialogueDirective(loc(nativeLine), spokenAccent));\n } else {\n const lines = (scene.dialogue ?? []).map((d) => d.line?.trim()).filter((l): l is string => Boolean(l));\n if (lines.length > 0)\n parts.push(`Spoken context (do not render as audio): ${lines.map((l) => `\"${l}\"`).join(\" \")}`);\n }\n const transcript = silentClipTranscript(scene, Boolean(nativeLine));\n if (transcript) parts.push(`Transcript: ${loc(transcript)}`);\n return parts;\n}\n\n/** What one generated clip shows and says: the input its prompt and its node are built from. */\nexport type ClipShot = {\n /** The scene, or one region's view of it. */\n scene: VideoScene;\n index: number;\n /** The region of a composited scene the clip renders; absent for a full-frame scene. */\n region?: number;\n /** The recurring elements the clip keeps consistent with their references. */\n present: ElementSlot[];\n mode: ShootMode;\n /** The line the model voices itself; undefined for b-roll or narration over the picture. */\n nativeLine?: string;\n /** Whether the clip is generated WITH native audio: a native line, or opt-in ambient b-roll. */\n audio: boolean;\n /**\n * The scene's screen/graphic surface rides the overlay layer (see uiRoutedSceneSet), so\n * the clip renders the background plate only.\n */\n uiRouted: boolean;\n};\n\n/**\n * Compose the ultra-detailed motion brief handed to the video model for one clip.\n *\n * When `shot.nativeLine` is set, the scene's single on-camera speaker is voiced by the\n * model NATIVELY: the line goes in double quotes (Seedance keys on quoted speech to\n * generate matching lip movements + voice), with `generate_audio` on the clip. The native\n * voice is then normalized to the brand voice downstream (`audio_voice_convert`). Without\n * one the scene is b-roll or narration-over-picture: the spoken lines are kept only as\n * context (the clip is generated silent) and the voiceover is a separate tts track.\n *\n * The prompt follows the video model's profile (dialogue syntax, model-specific hard\n * rules). Numerals are spelled into `env.ttsLanguageCode`'s words ONLY in text the model\n * SPEAKS (the quoted line and the transcript): Seedance has no language hint, so \"6930\" is\n * read in English even in a French line. Visual descriptions keep their digits: they\n * describe on-screen figures (a progress bar's \"4%\", a price tag), and spelling those\n * corrupted the picture (\"quattro% to diciassette%\").\n */\nexport function buildClipPrompt(\n shot: ClipShot,\n env: Pick<TimelineEnv, \"clipProfile\" | \"ttsLanguageCode\" | \"spokenAccent\">,\n): string {\n const { scene, nativeLine, present, mode } = shot;\n const profile = env.clipProfile;\n // On a native clip, spell numerals in the text Seedance SPEAKS; no-op for a silent clip.\n const loc = (s: string): string => (nativeLine ? localizeNumeralsForNative(s, env.ttsLanguageCode) : s);\n // A UI-routed composite renders as a plain background plate (the screen surface is\n // composited later as a REAL overlay layer) — so the brief must describe the PLATE\n // region only. The scene-level summary/action/motion narrate the overlay (\"share\n // options menu slides up…\"), and passing them through made Seedance hallucinate a\n // garbled UI underneath the real composited one.\n // With every region a UI surface (no camera plate at all) fall back to an empty\n // plate: a bare \"Scene N\" line plus the no-screen directive beats narrating the UI.\n const plate = shot.uiRouted ? (compositePlateRegion(scene) ?? {}) : null;\n const parts: string[] = visualBriefParts(scene, shot.index, plate);\n // On a plate clip, drop UI-surface elements from the consistency line — naming the\n // app's reference here is an instruction to render it.\n const refs = plate ? present.filter((s) => !UI_SURFACE_RE.test(s.description ?? \"\")) : present;\n if (refs.length > 0) {\n parts.push(\n `Keep these consistent with their references: ${refs.map((s) => `${s.label} (${s.description ?? s.type})`).join(\"; \")}`,\n );\n }\n parts.push(...spokenTextParts(scene, nativeLine, loc, profile, env.spokenAccent));\n const audioLine = seedanceAudioLine(scene, mode, shot.audio, nativeLine);\n if (audioLine) parts.push(audioLine);\n // Motion-brief directives: the start/end frames already encode the content, so\n // the clip prompt must describe MOTION ONLY and stay short — verbosity makes the\n // model skip beats. The camera default is shoot-mode specific (UGC locks; studio\n // and lifestyle may permit a slow motivated move), and a named move always wins.\n parts.push(\n `Direction: describe MOTION ONLY — the frames carry the content; keep it short. ${seedanceMotionFor(mode)} ` +\n \"Spell choreography move-by-move (not 'she dances' but the actual beats: head nod, shoulder roll, knee dip). \" +\n \"One short continuous beat with real physical weight on any impact. \" +\n \"Describe any person by role and wardrobe, never by name or age.\",\n );\n // The image-to-video keyframe carries the content; tell the model to hold it. Model-\n // specific: Seedance keeps composition/colors, Veo describes the transition instead.\n parts.push(profile.keyframeInstruction);\n // Models with no negative_prompt param (Seedance) carry anti-artifact intent as an\n // AFFIRMATIVE stability line — a positive \"keep it clean\" beats an \"Avoid:\" list, which\n // tends to plant the artifact it names (seedance-mechanics.md §3). Veo/Kling route this\n // through their negative_prompt param instead, so their list stays empty here.\n if (profile.stabilityDirectives.length > 0)\n parts.push(`Keep it clean and stable: ${profile.stabilityDirectives.join(\"; \")}.`);\n parts.push(...profile.extraDirectives);\n return parts.join(\"\\n\");\n}\n","import { canSpellNumbers, spellNumber } from \"../../../lib/spell-number.ts\";\n\n// --- Numeral → words, for NATIVE (Seedance) dialogue only. ---\n// A `tts` node gets `language_code`, so ElevenLabs reads its numbers in the right\n// tongue. But a NATIVE talking clip's line is voiced by Seedance, which has no\n// language hint — so a bare \"6930\" in a French line is read in English (\"six\n// thousand nine hundred thirty\"), the reported defect. Spelling the number into\n// target-language words in the QUOTED dialogue forces the right pronunciation (and\n// the lips follow the words). Every language the blueprint can resolve has a speller\n// (`engine/lib/spell-number.ts`); a language we can't resolve leaves digits (the `tts` path still\n// localizes them via language_code).\n\n/**\n * Spell standalone integers in `text` into `langCode` words, for a NATIVE spoken line.\n * Leaves the text untouched when there's no speller for the language. Only plain\n * integers up to 9 digits are converted (prices/counts); anything with a decimal,\n * separators, or an adjacent letter/digit/hyphen (e.g. \"1er\", a \"555-1234\" phone\n * number, a \"30-second\" range) is left alone — `-` is in the boundary set so neither\n * half of a hyphenated number is spelled in isolation.\n */\nexport function localizeNumeralsForNative(text: string, langCode: string | undefined): string {\n if (!langCode || !canSpellNumbers(langCode)) return text;\n return text.replace(/(?<![\\w.,-])\\d{1,9}(?![\\w.,-])/g, (m) => spellNumber(langCode, Number.parseInt(m, 10)));\n}\n","import { supportsLastFrame } from \"@baker/canvas-contract\";\nimport { clipParamRecipe } from \"../../../lib/prompt-profiles.ts\";\nimport type { NodeDecl } from \"../../../schema/canvas.ts\";\nimport { clipIntent } from \"../../lib/scene-role.ts\";\nimport { canvasDims } from \"../aspect.ts\";\nimport { emitAmbientExtract } from \"../dialogue/voices.ts\";\nimport { slotsForFrame } from \"../elements.ts\";\nimport { trimArgs } from \"../ffmpeg-args.ts\";\nimport { buildFrameRef } from \"../frames.ts\";\nimport { ids, ref, sceneTags } from \"../ids.ts\";\nimport { videoAudioParam, videoDurationParam, videoResolutionParam } from \"../model-params.ts\";\nimport { buildClipPrompt, type ClipShot } from \"../prompts/clip.ts\";\nimport type { SceneClip, SceneCtx, TimelineEnv } from \"../timeline-env.ts\";\n\n/** The keyframes a clip is conditioned on. A model that takes one image drops `last`. */\nexport type ClipFrames = { first: string; last?: string };\n\n/**\n * The `video_generate` node for one clip — every clip the scaffold bills is built here, the\n * b-roll shot, a composite's region and a presenter's phrase alike, so they cannot drift\n * apart in the params that decide what the provider renders and what the cache keys on.\n *\n * `genDur` is a length the model renders (`ceilToDurations`), at least the window the clip\n * fills. An end frame can exist purely as the NEXT scene's splice frame, so having one is not\n * permission to wire it: a model that takes a single conditioning image silently drops it.\n */\nexport function clipNode(shot: ClipShot, env: TimelineEnv, frames: ClipFrames, genDur: number): NodeDecl {\n const videoModel = env.opts.videoModel;\n return {\n id: ids.clip(shot.index, shot.region),\n type: \"video_generate\",\n tags: sceneTags(shot.index, \"clip\", shot.region),\n inputs: {\n first_frame: frames.first,\n ...(frames.last && supportsLastFrame(videoModel) ? { last_frame: frames.last } : {}),\n },\n params: {\n model: videoModel,\n // Per-model request defaults (Veo: person_generation=allow_adult + a caption-\n // suppressing negative_prompt). Spread first so the explicit fields below win.\n ...env.clipProfile.paramDefaults,\n prompt: buildClipPrompt(shot, env),\n ...videoDurationParam(videoModel, genDur),\n ...videoResolutionParam(videoModel, env.opts.resolution),\n // Native talking scene → the model generates the spoken audio + lip-sync; an opt-in\n // ambient b-roll beat generates diegetic ambient only; otherwise the clip is silent.\n ...videoAudioParam(videoModel, shot.audio),\n // Intent-keyed \"astonishing default\" overrides — merged LAST so a hero/reveal\n // beat can claim the 1080p ceiling over the aspect-derived resolution.\n ...clipParamRecipe(\n videoModel,\n env.clipProfile,\n clipIntent(shot.scene.narrative_role, shot.index, shot.scene.motion_level),\n ),\n aspect_ratio: env.genAr,\n },\n };\n}\n\n/**\n * Emit one scene's `video_generate` clip and return the SceneClip. Both keyframes are\n * wired (`first_frame` + `last_frame`) so the model interpolates real in-shot motion.\n * The clip is generated at one of the model's fixed durations (>= the scene length), so\n * it must be cut back to the EXACT scene length — a duration-snap so the picture stays on\n * the absolute-timed audio (and a clip generated at another ratio than the output canvas\n * is normalized onto it). A SPINE-destined clip (`defer`) carries that cut as\n * SceneClip.trim, absorbed into the `video_concat` spine's per-input chain, with no\n * standalone node; a clip consumed elsewhere (a composite region) still emits the\n * `s<i>_clip_trim` ffmpeg node, because its consumer isn't the spine.\n */\nexport function emitSceneClip(\n ctx: SceneCtx,\n shot: ClipShot,\n frames: ClipFrames,\n spine: { out: SceneClip[\"out\"]; defer: boolean },\n): SceneClip {\n const { env, lengths } = ctx;\n ctx.out.nodes.push(clipNode(shot, env, frames, lengths.genDur));\n const base = ref(ids.clip(shot.index, shot.region), \"video\");\n // A clip generated at a DIFFERENT ratio than the output canvas (Seedance has no 4:5)\n // must be normalized (scale-to-fill, center-crop) before it enters the spine — the\n // raw-clip fast path is only safe when the two ratios match.\n const normDims = env.genAr !== env.outAr ? canvasDims(env.outAr) : undefined;\n if (lengths.genDur === lengths.trimTarget && !normDims) return { ref: base, scene_s: lengths.dur, out: spine.out };\n // A spine-destined clip defers the duration-snap to the spine's per-input chain\n // (the spine always normalizes the raster, so the remap case is covered for free).\n if (spine.defer) {\n return {\n ref: base,\n scene_s: lengths.dur,\n out: spine.out,\n ...(lengths.genDur !== lengths.trimTarget ? { trim: { offset_s: 0 } } : {}),\n };\n }\n // Every model renders a fixed set of durations; this trims the generated clip down to\n // the exact window length so each segment sits flush on the timeline (and normalizes\n // remapped-ratio clips onto the output canvas).\n const trimId = ids.clipTrim(shot.index, shot.region);\n ctx.out.nodes.push({\n id: trimId,\n type: \"ffmpeg\",\n tags: sceneTags(shot.index, \"clip_trim\", shot.region),\n inputs: { clip: base },\n params: { args: trimArgs(lengths.trimTarget, 0, normDims), outputs: { video: { kind: \"video\", ext: \"mp4\" } } },\n });\n return { ref: ref(trimId, \"video\"), scene_s: lengths.dur, out: spine.out };\n}\n\n/**\n * A b-roll scene's conditioning keyframes. Independent start AND end frames (the model\n * interpolates real motion between them), each grounded on its OWN extracted frame —\n * no cross-frame cascade, renders in parallel. Two exceptions: a continuation of the\n * same shot starts from the PREVIOUS scene's end frame (seamless splice), and a SHORT\n * (≤4s, one Seedance floor clip) take skips the end keyframe entirely — over so short\n * a span the second keyframe adds no motion guidance, so it's a billed image that buys\n * nothing — unless the NEXT scene continues this take and needs the shared splice frame.\n */\nfunction brollKeyframes(\n ctx: SceneCtx,\n prevEndFrame: string | undefined,\n): { first: string; last: string | undefined; sharesPrevFrame: boolean } {\n const { env, scene, index } = ctx;\n const sharesPrevFrame = Boolean(scene.continues_previous && prevEndFrame);\n const first =\n sharesPrevFrame && prevEndFrame\n ? prevEndFrame\n : buildFrameRef(ctx, {\n edge: \"start\",\n url: scene.start_frame_asset?.url,\n prompt: scene.start_frame_prompt,\n present: slotsForFrame(env.slots, index, \"start\"),\n });\n // The end keyframe earns its cost two ways: the model interpolates between the\n // pair (and the shot is long enough to be worth guiding), or the NEXT scene\n // continues this take and splices from it. A one-image model (Omni) satisfies\n // neither on its own, so it only gets one when the splice needs it — otherwise\n // this is a billed image_generate per clip that the renderer discards.\n const nextContinues = Boolean(env.blueprint.scenes[index + 1]?.continues_previous);\n const guidesMotion = supportsLastFrame(env.opts.videoModel) && ctx.lengths.trimTarget > 4;\n const last =\n guidesMotion || nextContinues\n ? buildFrameRef(ctx, {\n edge: \"end\",\n url: scene.end_frame_asset?.url,\n prompt: scene.end_frame_prompt,\n present: slotsForFrame(env.slots, index, \"end\"),\n })\n : undefined;\n return { first, last, sharesPrevFrame };\n}\n\n/**\n * The `clip` route: a b-roll scene (no presenter shown) as its own silent clip — or, with\n * `ambient`, a clip carrying its diegetic ambience, extracted and laid deep under the bed.\n * Returns this scene's END keyframe ref so the next scene, if it is a continuation of the\n * same shot (`continues_previous`), can reuse it as its start frame for a seamless splice.\n */\nexport function emitClipScene(ctx: SceneCtx, prevEndFrame: string | undefined, ambient: boolean): string | undefined {\n const { env, index } = ctx;\n const { first, last, sharesPrevFrame } = brollKeyframes(ctx, prevEndFrame);\n const shot: ClipShot = {\n scene: ctx.scene,\n index,\n present: ctx.present,\n mode: ctx.mode,\n audio: ambient,\n uiRouted: env.uiRouted.has(index),\n };\n const clip = emitSceneClip(ctx, shot, { first, last }, { out: ctx.lengths.out, defer: true });\n if (ambient) emitAmbientExtract(ctx, ref(ids.clip(index), \"video\"));\n ctx.out.clips.push(sharesPrevFrame ? { ...clip, continuesFrame: true } : clip);\n ctx.out.routes.set(index, \"clip\");\n return last;\n}\n","import type { VideoBlueprint, VideoScene } from \"@baker/canvas-contract\";\nimport type { ClipPromptProfile } from \"../../lib/prompt-profiles.ts\";\nimport type { NodeDecl } from \"../../schema/canvas.ts\";\nimport { deriveShootMode, type ShootMode } from \"../lib/shoot-modes.ts\";\nimport type { AudioTrack } from \"./audio.ts\";\nimport { isOnCameraSpeaker } from \"./dialogue/speakers.ts\";\nimport type { NativeSegment } from \"./dialogue/voices.ts\";\nimport { type ElementSlot, slotsForScene } from \"./elements.ts\";\nimport type { ScaffoldVideoOpts } from \"./opts.ts\";\nimport { type SceneLengths, type SpineClock, sceneTiming } from \"./timing.ts\";\n\n// One concatenated scene clip: the picture ref, the scene's on-timeline length,\n// and its outgoing transition (null = hard cut). `scene_s` is the length the scene\n// occupies on the final timeline — the xfade overlap is consumed from EXTRA footage\n// (the clip is trimmed to `scene_s + out.dur`), so the totals stay timeline-exact.\nexport type SceneClip = {\n ref: string;\n scene_s: number;\n out: { xfade: string; dur: number } | null;\n // True when this clip's FIRST frame is the previous clip's LAST frame (a shared splice\n // frame from a length-split shot). The spine drops the duplicate per `opts.seamDedup`.\n continuesFrame?: boolean;\n // Per-input work the `video_concat` spine absorbs into its own chain instead of a\n // standalone ffmpeg node: cut a longer clip to the scene window (`trim`, its clip\n // `length: \"trim\"`) or hold a still image/screenshot for the scene length (`still`).\n // The input length is always `scene_s + out.dur`.\n trim?: { offset_s: number };\n still?: { fit: \"fill\" | \"pad\" };\n};\n\n/**\n * A line the video model voices itself, in the clip it generates: one on-camera speaker's\n * words, their spoken window on the reference clock, and the casting their voice resolves to.\n */\nexport type NativeTurn = {\n text: string;\n start_s: number;\n end_s: number;\n // The shared voice_select node casting this speaker (brand voice).\n voiceNode: string;\n};\n\n// `onCamera`: the speaker's own lip-synced voice (a native clip's converted audio), as\n// opposed to a read over the picture. Internal: stripped from the canvas metadata.\nexport type VoSeg = { slot: string; start_s: number; end_s: number; scene: number; speaker: string; onCamera: boolean };\nexport type TalkScene = {\n scene: number;\n voice_convert_node: string;\n scene_s?: number;\n est_speech_s?: number;\n speech_words?: number;\n};\n// A presenter scene's window cut from its phrase clip. `skip` marks a scene whose window\n// was FOLDED into a preceding scene's merged run-segment — the spine emits nothing for it\n// (its picture already rides the earlier segment), it exists only so the dispatch loop\n// doesn't fall the scene through to the b-roll path.\nexport type SliceInfo = {\n clipRef: string;\n offset: number;\n len: number;\n clipDur: number;\n skip?: boolean;\n // True when this slice's clip starts on the previous phrase clip's shared end frame (a\n // length-split of the same shot) — the spine seam-dedups the duplicated boundary frame.\n continuesFrame?: boolean;\n};\n\n/** The canvas-wide facts every scene is emitted against, each computed once per scaffold. */\nexport type TimelineEnv = {\n blueprint: VideoBlueprint;\n // The spine clock — every absolute placement (voice, SFX, ambient) maps through it.\n clock: SpineClock;\n slots: ElementSlot[];\n opts: ScaffoldVideoOpts;\n // The ad's true output ratio (drives every ffmpeg dim + composite canvas)…\n outAr: string;\n // …and the ratio clips/keyframes GENERATE at (the video model's nearest supported\n // ratio). When they differ, every clip is normalized to outAr before the spine.\n genAr: string;\n /** The clip lengths the video model renders (`clipDurationSet`); its longest is the scene ceiling. */\n durationSet: readonly number[];\n ceilingS: number;\n /** The video model's prompt profile: Seedance's when the model has none of its own. */\n clipProfile: ClipPromptProfile;\n reuse: boolean;\n /**\n * The source ad shows people somewhere (a cast, or a recurring person element).\n * Then no generated frame takes a source frame as a reference: which scenes a person\n * is in is a model's guess, and one frame it misses hands the image model the source\n * actor's face, which it copies whatever the prompt says.\n */\n sourceShowsPeople: boolean;\n cameraOn: boolean;\n casts: Set<string>;\n /** Folds a speaker label onto the one voice it belongs to (see `collapseVoiceover`). */\n canonical: (speaker: string) => string;\n // Shared across all frames in a canvas: a source-frame URL → its ingest ref, so\n // the same extracted frame is ingested ONCE and reused (no duplicate ingest nodes\n // when two scenes happen to share a frame URL).\n ingestCache: Map<string, string>;\n // Scene indexes whose screen/graphic surface routes to the overlay layer (per\n // persistent layout, see uiRoutedSceneSet) — their clips render the plate only.\n uiRouted: Set<number>;\n // Scene indexes reproduced as a real designed-graphic plate (typed full-frame\n // static_graphic scenes, closed over continues_previous chains — one take is one\n // content class, so an untyped chain member inherits its sibling's graphic route).\n graphicScenes: Set<number>;\n // surface_id → ingest node id: scenes sharing one checker-owned surface reuse ONE\n // supplied design asset instead of stubbing a TODO ingest per scene.\n surfaceIngests: Map<string, string>;\n // ISO 639-1 of the spoken track (when known), stamped onto every tts node.\n ttsLanguageCode?: string;\n // The spoken language's English name, the hard filter `voice_select` casts on.\n spokenLanguageName?: string;\n spokenAccent?: string;\n /** See `ScaffoldVideoOpts.keepRenderedVoice`. */\n keepRenderedVoice?: boolean;\n};\n\n// The mutable sinks the timeline helpers append to.\nexport type TimelineOut = {\n nodes: NodeDecl[];\n clips: SceneClip[];\n voTracks: AudioTrack[];\n voSegments: VoSeg[];\n talkingScenes: TalkScene[];\n nativeSegments: NativeSegment[];\n sceneSlice: Map<number, SliceInfo>;\n // Per-scene route the dispatcher took — stamped into metadata.video.graph_stats so\n // an operator can see WHY each scene got its nodes (the \"why so many nodes\" answer).\n routes: Map<number, SceneRoute>;\n // The previous phrase clip's END keyframe ref and the scene it depicts, so a\n // mid-take follow-up phrase can chain from it (shared splice frame, no jump).\n phraseEnd?: { ref: string; scene: number };\n /** The casting of each speaker: returns their voice_select node, emitting it on first use. */\n voiceOf: (speaker: string) => string;\n /** Ids already taken by `tts` reads, so two reads of one speaker never collide. */\n readIds: Set<string>;\n};\n\n// How a scene's picture is produced. clip = its own billed video_generate; still_hold =\n// a generated keyframe looped on the spine; screen_still / graphic_plate = a REAL\n// ingest looped on the spine; brand_card = a colour plate; phrase_slice = a window cut\n// from its phrase's talking clip; phrase_run = folded into a preceding merged slice;\n// composite = stacked/overlaid region clips.\nexport type SceneRoute =\n | \"clip\"\n | \"still_hold\"\n | \"screen_still\"\n | \"graphic_plate\"\n | \"brand_card\"\n | \"phrase_slice\"\n | \"phrase_run\"\n | \"composite\";\n\n/** One scene, as every route that emits it sees it: the canvas, the sinks, and what the scene is. */\nexport type SceneCtx = {\n env: TimelineEnv;\n out: TimelineOut;\n index: number;\n scene: VideoScene;\n isLast: boolean;\n /** The recurring-element slots present anywhere in the scene. */\n present: ElementSlot[];\n mode: ShootMode;\n lengths: SceneLengths;\n};\n\n/** The shoot mode for a scene: an explicit override wins, else auto-derive from talking/cast/product presence. */\nfunction sceneShootMode(\n scene: VideoScene,\n present: ElementSlot[],\n nativeTurn: NativeTurn | undefined,\n env: TimelineEnv,\n): ShootMode {\n // A \"talking\" beat (→ selfie look) = an on-camera cast member speaks: native\n // single-speaker OR a multi-speaker on-camera scene.\n const talking =\n Boolean(nativeTurn) ||\n (env.cameraOn &&\n (scene.dialogue ?? []).some(\n (d) => d.line?.trim() && isOnCameraSpeaker(d.speaker ?? \"voiceover\", env.casts, env.cameraOn),\n ));\n return deriveShootMode({\n explicit: scene.shoot_mode,\n talking,\n hasPerson: present.some((s) => s.type.toLowerCase() === \"person\" || s.type.toLowerCase() === \"animal\"),\n hasProduct: present.some((s) => s.type.toLowerCase() === \"product\"),\n });\n}\n\n/** The context scene `index` is emitted in, given the line (if any) the video model voices in it. */\nexport function sceneCtx(env: TimelineEnv, out: TimelineOut, index: number, nativeTurn?: NativeTurn): SceneCtx {\n const scene = env.blueprint.scenes[index];\n if (!scene) throw new Error(`scaffold invariant: scene ${index} does not exist`);\n const present = slotsForScene(env.slots, index);\n const isLast = index === env.blueprint.scenes.length - 1;\n return {\n env,\n out,\n index,\n scene,\n isLast,\n present,\n mode: sceneShootMode(scene, present, nativeTurn, env),\n lengths: sceneTiming(scene, isLast, nativeTurn?.text, env.durationSet),\n };\n}\n","import {\n type BlueprintDialogueLine,\n type CompositionRegion,\n type SceneComposition,\n supportsLastFrame,\n type VideoScene,\n} from \"@baker/canvas-contract\";\nimport { estSpeechS, estSpeechWindowS, wordCount } from \"../../../lib/speech.ts\";\nimport { canvasDims } from \"../aspect.ts\";\nimport { isOnCameraSpeaker } from \"../dialogue/speakers.ts\";\nimport { emitNativeExtract, emitPhraseTts } from \"../dialogue/voices.ts\";\nimport type { ElementSlot } from \"../elements.ts\";\nimport { pipOverlayArgs, splitStackArgs } from \"../ffmpeg-args.ts\";\nimport { buildFrameRef } from \"../frames.ts\";\nimport { ids, ref, sceneTags } from \"../ids.ts\";\nimport { compositeRegionsOf } from \"../routing.ts\";\nimport {\n type NativeTurn,\n type SceneClip,\n type SceneCtx,\n sceneCtx,\n type TimelineEnv,\n type TimelineOut,\n} from \"../timeline-env.ts\";\nimport { sceneDurationS } from \"../timing.ts\";\nimport { emitSceneClip } from \"./clip.ts\";\n\n/** A scene's spatial layout when it is layered: which layout, its regions, and the raw composition. */\nexport type LayeredComposition = { layout: string; regions: CompositionRegion[]; comp: SceneComposition };\n\n/** A scene's layered composition, or null when it's a plain full-frame shot. */\nexport function layeredComposition(\n scene: VideoScene,\n // True when this scene's layout run is UI-routed (see uiRoutedSceneSet): the real\n // screen/graphic surface is composited on the overlay layer, so the scene drops to\n // the single-shot path (one clean background plate) — a Seedance split would garble\n // the UI and leave a stack seam.\n uiRouted: boolean,\n): LayeredComposition | null {\n const regions = compositeRegionsOf(scene);\n // Need ≥2 streams to composite; otherwise fall back to the single-shot path.\n if (!regions) return null;\n if (uiRouted) return null;\n return { layout: (scene.composition?.layout ?? \"\").toLowerCase(), regions, comp: scene.composition ?? {} };\n}\n\n/**\n * Stack direction for a split, derived from the region PANELS (authoritative — a\n * model labels top/bottom reliably but conflates split_axis \"horizontal\" with a\n * horizontal dividing LINE). top/bottom ⇒ vertical (vstack); left/right ⇒ horizontal\n * (hstack); fall back to split_axis only when panels are unlabeled.\n */\nfunction splitAxisOf(comp: SceneComposition, regions: CompositionRegion[]): \"vertical\" | \"horizontal\" {\n const panels = regions.map((r) => (r.panel ?? \"\").toLowerCase());\n if (panels.some((p) => p === \"top\" || p === \"bottom\")) return \"vertical\";\n if (panels.some((p) => p === \"left\" || p === \"right\")) return \"horizontal\";\n return (comp.split_axis ?? \"\").toLowerCase() === \"horizontal\" ? \"horizontal\" : \"vertical\";\n}\n\n/** A panel label's visual rank along the stack axis (numbered labels are explicit). */\nfunction panelRank(panel: string | undefined, axis: \"vertical\" | \"horizontal\"): number {\n const p = (panel ?? \"\").toLowerCase();\n // A numbered label (\"panel 2\", \"p3\") is an explicit order — honor it directly.\n const numbered = /(\\d+)/.exec(p);\n if (numbered) return Number(numbered[1]);\n if (axis === \"vertical\") return p === \"top\" ? 0 : p === \"bottom\" ? 2 : 1;\n return p === \"left\" ? 0 : p === \"right\" ? 2 : 1;\n}\n\n/**\n * Order split-screen region refs into VISUAL stacking order for the axis: top before\n * bottom (vertical), left before right (horizontal). The `panel` label carries the\n * placement; the `regions` array order does not, so without this a model that lists the\n * bottom/right panel first would render the split inverted. A stable sort keeps the\n * original order among unlabeled panels.\n */\nexport function orderSplitRefs(\n regions: CompositionRegion[],\n regionRefs: string[],\n axis: \"vertical\" | \"horizontal\",\n): string[] {\n return regionRefs\n .map((input, k) => ({ input, k, rank: panelRank(regions[k]?.panel, axis) }))\n .sort((a, b) => a.rank - b.rank || a.k - b.k)\n .map((x) => x.input);\n}\n\n// A region holds the presenter (the lip-synced talking head) when it's flagged\n// is_presenter; a person/animal region wins by default when a scene has spoken audio\n// but the model forgot the flag (so the voice always lands on a real mouth).\nfunction presenterIndexOf(regions: CompositionRegion[], hasNative: boolean): number {\n const flagged = regions.findIndex((r) => r.is_presenter);\n if (flagged >= 0) return flagged;\n return hasNative ? 0 : -1;\n}\n\n/** The recurring-element slots that belong to ONE region: the presenter region gets\n * the people/animals present in the scene; b-roll/background regions get the sets,\n * products, and logos (so the right reference grounds the right panel). */\nfunction slotsForRegion(present: ElementSlot[], isPresenter: boolean): ElementSlot[] {\n return present.filter((s) => {\n const t = s.type.toLowerCase();\n const person = t === \"person\" || t === \"animal\";\n return isPresenter ? person : !person;\n });\n}\n\ntype SpokenLine = BlueprintDialogueLine & { line: string };\n\n/**\n * Who speaks on camera in a composite scene. The video model voices ONE mouth per clip,\n * so a composite with two+ distinct on-camera speakers can't be voiced natively under a\n * single voice (that would put both lines in the first speaker's voice). Only a\n * single-voice composite goes native; a multi-speaker one renders silent and reads each\n * speaker as a tts track, mirroring the plain-scene multi-speaker fallback in `buildPhrases`.\n */\nexport function compositeVoicing(\n scene: VideoScene,\n env: Pick<TimelineEnv, \"casts\" | \"cameraOn\" | \"canonical\">,\n): { lines: SpokenLine[]; speakers: Set<string>; native: boolean } {\n const lines = (scene.dialogue ?? []).filter(\n (l): l is SpokenLine =>\n Boolean(l.line?.trim()) && isOnCameraSpeaker(l.speaker ?? \"voiceover\", env.casts, env.cameraOn),\n );\n const speakers = new Set(lines.map((l) => env.canonical(l.speaker ?? \"voiceover\")));\n return { lines, speakers, native: lines.length > 0 && speakers.size === 1 };\n}\n\n/**\n * Reproduce a COMPOSITED scene: build one clip per region (its own frames + motion\n * brief), then stack (split-screen) or overlay (PIP / keyed presenter) them into one\n * picture. Returns the composited scene clip plus the PRESENTER region's raw clip ref\n * (the one carrying the spoken audio) so the per-speaker voice conversion extracts\n * from the right panel. Generate-mode only — `reuse` keeps the single-shot path.\n */\nfunction buildCompositeScene(\n ctx: SceneCtx,\n composite: LayeredComposition,\n nativeTurn: NativeTurn | undefined,\n): { clip: SceneClip; presenterClipRef: string | undefined } {\n const { env, scene, index } = ctx;\n const { layout, regions, comp } = composite;\n const presIdx = presenterIndexOf(regions, Boolean(nativeTurn));\n const regionRefs = regions.map((region, r) => {\n const isPresenter = r === presIdx;\n const regionSlots = slotsForRegion(ctx.present, isPresenter);\n // Independent start AND end keyframes (the model interpolates real in-shot motion).\n // Both ground only on el_*/recast — no cross-frame reference — so they render in parallel.\n // A one-image model gets no end frame: it would be a billed image_generate PER\n // REGION that the renderer discards.\n const first = buildFrameRef(ctx, {\n edge: \"start\",\n url: undefined,\n prompt: region.frame_prompt ?? scene.start_frame_prompt,\n present: regionSlots,\n region: r,\n });\n const last = supportsLastFrame(env.opts.videoModel)\n ? buildFrameRef(ctx, {\n edge: \"end\",\n url: undefined,\n prompt: region.frame_prompt ?? scene.end_frame_prompt,\n present: regionSlots,\n region: r,\n })\n : undefined;\n // The b-roll/background panels are silent; only the presenter panel speaks.\n const regionNative = isPresenter ? nativeTurn : undefined;\n const regionScene = {\n ...scene,\n summary: region.summary ?? scene.summary,\n motion_prompt: region.motion_prompt ?? scene.motion_prompt,\n dialogue: isPresenter ? scene.dialogue : [],\n };\n // A composite scene is never UI-routed: a scene whose own regions route to the overlay\n // layer is dropped to the single-shot path before it gets here (`layeredComposition`).\n const shot = {\n scene: regionScene,\n index,\n region: r,\n present: regionSlots,\n mode: ctx.mode,\n nativeLine: regionNative?.text,\n audio: Boolean(regionNative),\n uiRouted: false,\n };\n return emitSceneClip(ctx, shot, { first, last }, { out: null, defer: false }).ref;\n });\n\n const dims = canvasDims(env.outAr);\n const compInputs: Record<string, unknown> = {};\n let args: string[];\n if (layout === \"split_screen\") {\n const axis = splitAxisOf(comp, regions);\n // Stack panels in VISUAL order (top→bottom / left→right) per their `panel` label,\n // not in the order the model happened to list `regions` — splitStackArgs places c0\n // first (top/left), so an unordered array would render the split inverted.\n orderSplitRefs(regions, regionRefs, axis).forEach((input, k) => {\n compInputs[`c${k}`] = input;\n });\n args = splitStackArgs(regionRefs.length, axis, dims);\n } else {\n args = overlayRegions(ctx, composite, regionRefs, presIdx, compInputs);\n }\n const compositeId = ids.composite(index);\n ctx.out.nodes.push({\n id: compositeId,\n type: \"ffmpeg\",\n tags: sceneTags(index, \"composite\"),\n inputs: compInputs,\n params: { args, outputs: { video: { kind: \"video\", ext: \"mp4\" } } },\n });\n return {\n clip: { ref: ref(compositeId, \"video\"), scene_s: ctx.lengths.dur, out: ctx.lengths.out },\n presenterClipRef: presIdx >= 0 ? ref(ids.clip(index, presIdx), \"video\") : undefined,\n };\n}\n\n/**\n * pip / keyed_overlay: one region is the full-frame background, EVERY other region rides\n * on top as an inset (the presenter keyed = cut to transparency first). All regions\n * composite — each is a billed generated clip, and dropping any of them paid for a clip\n * that never reached the frame. Fills `compInputs` (c0 = background, c1.. = insets) and\n * returns the overlay argv.\n */\nfunction overlayRegions(\n ctx: SceneCtx,\n { layout, regions }: LayeredComposition,\n regionRefs: string[],\n presIdx: number,\n compInputs: Record<string, unknown>,\n): string[] {\n const bgIdx = regions.findIndex((_, k) => k !== presIdx);\n const effBgIdx = bgIdx >= 0 ? bgIdx : 0;\n // With no flagged presenter (presIdx -1) the schema convention holds: background\n // first, the overlaid region second — so the fallback presenter is regions[1].\n const effPresIdx = presIdx >= 0 ? presIdx : 1;\n // Only a flagged (or native) presenter's own position anchors it; the fallback one\n // takes the next free corner like any unlabeled inset.\n const presenterPosition = presIdx >= 0 ? regions[presIdx]?.position : undefined;\n compInputs.c0 = regionRefs[effBgIdx];\n // Unlabeled extra insets cascade through distinct corners so they never stack\n // on the presenter's slot.\n const cornerCascade = [\"top_left\", \"top_right\", \"bottom_left\", \"bottom_right\"];\n let cornerCursor = 0;\n const insets: Array<{ position?: string; wpct: number }> = [];\n regions.forEach((region, r) => {\n if (r === effBgIdx) return;\n const isPresenter = r === effPresIdx;\n let input = regionRefs[r];\n if (isPresenter && layout === \"keyed_overlay\") {\n // The green-screen look: strip the presenter's background to alpha, then overlay.\n // Applied to the FALLBACK presenter too — skipping the key when no region was\n // flagged is_presenter shipped an opaque box instead of a cut-out.\n const keyId = ids.key(ctx.index);\n ctx.out.nodes.push({\n id: keyId,\n type: \"video_background_remove\",\n tags: sceneTags(ctx.index, \"key\", r),\n inputs: { video: input },\n params: {},\n });\n input = ref(keyId, \"video\");\n }\n compInputs[`c${insets.length + 1}`] = input;\n const fallback = cornerCascade[cornerCursor++ % cornerCascade.length];\n insets.push({\n position: (isPresenter ? presenterPosition : region.position) ?? fallback,\n // Keyed cut-outs read as a larger presence than a boxed PIP pill.\n wpct: isPresenter && layout === \"keyed_overlay\" ? 0.5 : 0.34,\n });\n });\n return pipOverlayArgs(canvasDims(ctx.env.outAr), insets);\n}\n\n/**\n * The `composite` route: a split-screen / PIP / keyed-presenter scene, voiced on its own —\n * its single on-camera speaker natively (their line in the presenter region's clip, then\n * the per-speaker convert), or each of several speakers as their own tts read over the\n * silent composite. The caller breaks the continuation chain (a composite has no single\n * splice frame).\n */\nexport function emitCompositeScene(\n env: TimelineEnv,\n out: TimelineOut,\n index: number,\n composite: LayeredComposition,\n): void {\n const scene = env.blueprint.scenes[index];\n if (!scene) return;\n const voicing = compositeVoicing(scene, env);\n const [firstLine] = voicing.lines;\n let nativeTurn: NativeTurn | undefined;\n if (voicing.native && firstLine) {\n const lastLine = voicing.lines.at(-1) ?? firstLine;\n const start = firstLine.start_s ?? scene.start_s ?? 0;\n const end = lastLine.end_s ?? scene.end_s ?? start;\n const text = voicing.lines.map((l) => l.line.trim()).join(\" \");\n const voiceNode = out.voiceOf(env.canonical(firstLine.speaker ?? \"voiceover\"));\n nativeTurn = { text, start_s: start, end_s: end, voiceNode };\n out.talkingScenes.push({\n scene: index,\n voice_convert_node: ids.voiceConv(voiceNode),\n scene_s: Math.round(sceneDurationS(scene, env.ceilingS) * 100) / 100,\n est_speech_s: Math.round(estSpeechWindowS(text, start, end) * 100) / 100,\n speech_words: wordCount(text),\n });\n }\n const ctx = sceneCtx(env, out, index, nativeTurn);\n const built = buildCompositeScene(ctx, composite, nativeTurn);\n if (nativeTurn) {\n if (!built.presenterClipRef) {\n throw new Error(`scaffold invariant: scene ${index} has a native turn but no clip to extract audio from`);\n }\n emitNativeExtract(ctx, nativeTurn, { ref: built.presenterClipRef, lengthS: ctx.lengths.genDur });\n }\n out.clips.push(built.clip);\n if (!nativeTurn && voicing.speakers.size >= 2) emitCompositeReads(ctx, voicing.lines);\n out.routes.set(index, \"composite\");\n}\n\n/**\n * Voice a multi-speaker composite scene: one `tts` read per distinct speaker over the\n * (silent) composite clip. The clip carries no native speech, so — exactly like the\n * plain-scene multi-speaker path — each speaker's lines are read in their own brand\n * voice instead of being collapsed into one.\n */\nfunction emitCompositeReads(ctx: SceneCtx, lines: SpokenLine[]): void {\n const { env, out, scene, index } = ctx;\n const bySpeaker = new Map<string, { lines: string[]; start: number; end: number }>();\n for (const l of lines) {\n const speaker = env.canonical(l.speaker ?? \"voiceover\");\n const text = l.line.trim();\n const start = l.start_s ?? scene.start_s ?? 0;\n const end = l.end_s ?? start + estSpeechS(text);\n const cur = bySpeaker.get(speaker);\n if (cur) {\n cur.lines.push(text);\n cur.start = Math.min(cur.start, start);\n cur.end = Math.max(cur.end, end);\n } else {\n bySpeaker.set(speaker, { lines: [text], start, end });\n }\n }\n for (const [speaker, agg] of bySpeaker) {\n emitPhraseTts(\n {\n speaker,\n start_s: agg.start,\n end_s: agg.end,\n text: agg.lines.join(\" \"),\n firstScene: index,\n shownScenes: [],\n presenterShown: false,\n },\n out.voiceOf(speaker),\n index,\n env,\n out,\n );\n }\n}\n","import { supportsLastFrame } from \"@baker/canvas-contract\";\nimport { estSpeechWindowS, wordCount } from \"../../../lib/speech.ts\";\nimport type { Phrase } from \"../dialogue/phrases.ts\";\nimport { emitNativeExtract } from \"../dialogue/voices.ts\";\nimport { slotsForFrame } from \"../elements.ts\";\nimport { buildFrameRef } from \"../frames.ts\";\nimport { ids, ref } from \"../ids.ts\";\nimport { type SceneCtx, type SliceInfo, sceneCtx, type TimelineEnv, type TimelineOut } from \"../timeline-env.ts\";\nimport { ceilToDurations, sceneDurationS } from \"../timing.ts\";\nimport { clipNode } from \"./clip.ts\";\n\n/**\n * A presenter-shown phrase: ONE clip over the whole phrase (native lip-sync + audio) →\n * extract → voice_convert to the brand voice. Records, for each shown scene, the slice\n * (offset+length) to cut its window out of this clip. The clip is named for the phrase's\n * first shown scene (`s<anchor>_clip`) so the validator's native-audio check finds it,\n * and its keyframes carry that scene's id and style, the end frame included.\n */\nexport function emitPhraseClip(phrase: Phrase, voiceNode: string, env: TimelineEnv, out: TimelineOut): void {\n const [anchor] = phrase.shownScenes;\n if (anchor === undefined || !env.blueprint.scenes[anchor]) return;\n const ctx = sceneCtx(env, out, anchor, {\n text: phrase.text,\n start_s: phrase.start_s,\n end_s: phrase.end_s,\n voiceNode,\n });\n const anchorScene = ctx.scene;\n // A phrase whose anchor CONTINUES the previous scene's camera take starts from the\n // PREVIOUS phrase's end keyframe — the join sits mid-take (an LLM content beat, not\n // a real cut), and two independently generated keyframes there produced a visible\n // identity/pose jump inside one continuous shot (audit defect A7).\n const chained = anchorScene.continues_previous && out.phraseEnd?.scene === anchor - 1 ? out.phraseEnd.ref : undefined;\n const first =\n chained ??\n buildFrameRef(ctx, {\n edge: \"start\",\n url: anchorScene.start_frame_asset?.url,\n prompt: anchorScene.start_frame_prompt,\n present: slotsForFrame(env.slots, anchor, \"start\"),\n });\n // The phrase clip gets a last_frame when the model can USE one (Seedance/Veo/Kling\n // interpolate real in-shot motion between the pair) — the END frame of the phrase's\n // last shown scene, grounded on its OWN extracted frame (no cross-frame cascade,\n // renders in parallel with `first`).\n //\n // On a one-image model (Omni) it is generated ONLY when the next scene splices from\n // it. Building it unconditionally billed an `image_generate` per clip for a frame\n // the renderer drops — the exact waste `VIDEO_MODEL_IGNORES_INPUT` warns about, and\n // the default scaffold path was tripping our own warning on every clip.\n const lastShown = phrase.shownScenes.at(-1) ?? anchor;\n const lastScene = env.blueprint.scenes[lastShown] ?? anchorScene;\n const nextSplicesFromEnd = Boolean(env.blueprint.scenes[lastShown + 1]?.continues_previous);\n const last =\n supportsLastFrame(env.opts.videoModel) || nextSplicesFromEnd\n ? buildFrameRef(ctx, {\n edge: \"end\",\n url: lastScene.end_frame_asset?.url,\n prompt: lastScene.end_frame_prompt,\n present: slotsForFrame(env.slots, lastShown, \"end\"),\n })\n : undefined;\n // The clip is anchored at the EARLIEST shown scene's visual start — not the line's\n // start. A scene whose spoken line begins well after its visual start (a silent lead-in)\n // must still have its whole window generated; sizing the clip from the line start left\n // the lead-in ungenerated and made the clip too short for the scene (truncated picture +\n // spine under-run). It must also cover every sliced scene's end. Every slice offset is\n // then measured from this same origin, so offsets are always ≥ 0.\n const clipStart = phrase.shownScenes.reduce(\n (m, s) => Math.min(m, env.blueprint.scenes[s]?.start_s ?? phrase.start_s),\n phrase.start_s,\n );\n const coverEnd = phrase.shownScenes.reduce((m, s) => Math.max(m, env.blueprint.scenes[s]?.end_s ?? 0), phrase.end_s);\n const phraseLen = Math.max(0.5, coverEnd - clipStart);\n // Clip length = the COVERAGE window (buildPhrases caps it at the Seedance ceiling). We\n // do NOT pad to a word-count speech estimate — that bloated the clip past its window,\n // which is what made the extracted voice spill into the next phrase. The model paces the\n // quoted text to fit, keeping the original ad's timing.\n const genDur = ceilToDurations(env.durationSet, phraseLen);\n out.nodes.push(\n clipNode(\n {\n scene: anchorScene,\n index: anchor,\n present: ctx.present,\n mode: ctx.mode,\n nativeLine: phrase.text,\n audio: true,\n uiRouted: env.uiRouted.has(anchor),\n },\n env,\n { first, last },\n genDur,\n ),\n );\n // Only publish a splice frame when one exists — the next phrase reads this to\n // continue the take, and a missing ref would dangle.\n out.phraseEnd = last ? { ref: last, scene: lastShown } : undefined;\n const clipRef = ref(ids.clip(anchor), \"video\");\n // Extract ONLY the spoken window (pause to pause), never the whole clip — extracting\n // the full clip ran the converted voice the clip's whole length and overlapped the\n // next phrase's voice. Skip the silent lead-in (when the clip starts before the line)\n // so the extracted voice is the speech, not the lead-in. The brand-voice conversion is\n // deferred to `buildPerSpeakerVoiceConversion`, which re-voices ALL of a speaker's\n // windows in ONE pass (same as composite scenes) instead of a convert node per clip:\n // fewer nodes, fewer ElevenLabs calls, and a MORE consistent brand voice (the model\n // keys the timbre off the whole track, not each clip in isolation).\n emitNativeExtract(\n ctx,\n { text: phrase.text, start_s: phrase.start_s, end_s: phrase.end_s, voiceNode },\n {\n ref: clipRef,\n lengthS: genDur,\n offsetS: Math.max(0, phrase.start_s - clipStart),\n },\n );\n // The per-speaker convert node is `<voiceNode>_conv`. Placement is on the SPINE clock:\n // the phrase starts where its anchor scene's picture actually sits in the butted concat,\n // not at its reference timestamp.\n const convId = ids.voiceConv(voiceNode);\n const spineStart = env.clock.map(anchor, phrase.start_s);\n out.voSegments.push({\n slot: convId,\n start_s: spineStart,\n end_s: spineStart + (phrase.end_s - phrase.start_s),\n scene: anchor,\n speaker: phrase.speaker,\n onCamera: true,\n });\n out.talkingScenes.push({\n scene: anchor,\n voice_convert_node: convId,\n scene_s: Math.round(phraseLen * 100) / 100,\n est_speech_s: Math.round(estSpeechWindowS(phrase.text, phrase.start_s, phrase.end_s) * 100) / 100,\n speech_words: wordCount(phrase.text),\n });\n registerMergedPhraseSlices(\n ctx,\n phrase.shownScenes,\n { ref: clipRef, start: clipStart, dur: genDur },\n Boolean(chained),\n );\n}\n\n/**\n * A merged run's picture length on the SPINE clock: the sum of its scenes' sceneDurationS,\n * NOT the raw reference span (last.end_s - first.start_s). The spine places every scene's\n * picture at Σ sceneDurationS, so a duration_s override, a sub-0.5s clamp, or a gap between\n * scenes makes the raw span diverge from where the audio/captions sit — desyncing the merged\n * picture and failing checkSpineTotal. Summing the same sceneDurationS the clock uses keeps\n * this slice on the one clock.\n */\nfunction sumSceneDurations(env: TimelineEnv, first: number, last: number): number {\n let total = 0;\n for (let s = first; s <= last; s++) {\n const sc = env.blueprint.scenes[s];\n if (sc) total += sceneDurationS(sc, env.ceilingS);\n }\n return total;\n}\n\n/** Scene indexes grouped into runs of consecutive indexes, in order. */\nfunction consecutiveRuns(scenes: readonly number[]): Array<{ first: number; last: number }> {\n const runs: Array<{ first: number; last: number }> = [];\n for (const scene of [...scenes].sort((a, b) => a - b)) {\n const run = runs.at(-1);\n if (run && scene === run.last + 1) run.last = scene;\n else runs.push({ first: scene, last: scene });\n }\n return runs;\n}\n\n/**\n * Register a phrase clip's scene slices, MERGING consecutive shown scenes into ONE segment.\n * A phrase clip is a single continuous take; slicing it back into a segment per\n * micro-scene and re-concatenating them in the spine is pure round-trip waste — the\n * deconstruct's shot-cuts inside one take aren't real cuts, and presenter slices never carry\n * a transition. So a run of index-consecutive shown scenes registers ONE slice on its first\n * scene (covering the whole run) and marks the rest `skip`; a run that tiles the whole clip\n * then rides the RAW clip (whole-clip fast path) with no `_seg` at all. `chained` flags the\n * ANCHOR slice (the phrase's first) as sharing the previous phrase clip's end frame, so the\n * spine seam-dedups that one duplicated boundary frame.\n */\nfunction registerMergedPhraseSlices(\n ctx: SceneCtx,\n shownScenes: readonly number[],\n clip: { ref: string; start: number; dur: number },\n chained: boolean,\n): void {\n const { env, out } = ctx;\n let firstRegistered = true;\n for (const { first, last } of consecutiveRuns(shownScenes)) {\n const firstSc = env.blueprint.scenes[first];\n if (!firstSc) continue;\n const rawOffset = (firstSc.start_s ?? clip.start) - clip.start;\n out.sceneSlice.set(first, {\n clipRef: clip.ref,\n // Snap a sub-frame offset (line-start vs scene-start drift) to 0 so a run that tiles\n // the clip hits the whole-clip fast path instead of a needless re-encode + tiny shift.\n offset: rawOffset < 0.05 ? 0 : rawOffset,\n len: Math.max(0.5, sumSceneDurations(env, first, last)),\n clipDur: clip.dur,\n ...(firstRegistered && chained ? { continuesFrame: true } : {}),\n });\n firstRegistered = false;\n // The rest of the run: no segment of their own — their picture rides the merged one.\n for (let s = first + 1; s <= last; s++) {\n out.sceneSlice.set(s, { clipRef: clip.ref, offset: 0, len: 0, clipDur: clip.dur, skip: true });\n }\n }\n}\n\n/**\n * The `phrase_slice` and `phrase_run` routes: a presenter scene's spine clip is its window\n * cut out of the phrase clip (video only; the phrase's converted voice plays underneath —\n * same clip ⇒ lip-sync holds). A `skip` scene (`phrase_run`) was folded into a preceding\n * merged run-segment, so it emits nothing. When the slice IS the whole clip (offset 0,\n * exact length) the raw clip is the segment; otherwise the window cut rides the spine's\n * per-input chain (SceneClip.trim) — no standalone `s<i>_seg` node, and the spine's\n * normalize covers any ratio remap.\n */\nexport function emitSliceScene(index: number, slice: SliceInfo, out: TimelineOut): void {\n out.routes.set(index, slice.skip ? \"phrase_run\" : \"phrase_slice\");\n if (slice.skip) return;\n const whole = slice.offset === 0 && Math.abs(slice.len - slice.clipDur) <= 0.05;\n out.clips.push({\n ref: slice.clipRef,\n scene_s: slice.len,\n out: null,\n ...(slice.continuesFrame ? { continuesFrame: true } : {}),\n ...(whole ? {} : { trim: { offset_s: slice.offset } }),\n });\n}\n","import { isCtaRole } from \"../../lib/scene-role.ts\";\nimport { ids } from \"../ids.ts\";\nimport { UI_SURFACE_RE } from \"../routing.ts\";\nimport type { SceneCtx } from \"../timeline-env.ts\";\nimport { emitSurfaceStill } from \"./surface-still.ts\";\n\n/**\n * A FULL-FRAME app/UI/website/chat screen scene (the reference's screen-recording\n * beats — quote checklist, claim-approved card, coverage screen). The deconstruct\n * describes these as \"a clean mobile app interface …\", and the scaffold MUST NOT\n * route them to image_generate/video_generate: a generative model garbles UI text\n * and brand chrome (CCA-07/08 — the baked \"Demande acceptée\" pill, the sliced\n * caption). They are reproduced from a REAL screenshot fit-padded full-frame.\n * Gated on NO on-camera person/animal being present (a screen beat has no cast),\n * so a talking head that merely mentions an app is never misrouted.\n */\nexport function sceneIsFullScreenUi(ctx: SceneCtx): boolean {\n const { scene } = ctx;\n // An explicit CTA is a brand end-card, never a screen recording — even when its copy\n // mentions a \"website URL\" or \"app\". Yield so the dispatch falls through to the\n // brand-card route (a clean colour plate + the overlay's crisp wordmark/URL); otherwise\n // the screen route would ask the operator to screenshot a URL that doesn't exist.\n if (isCtaRole(scene.narrative_role)) return false;\n const hasCast = ctx.present.some((s) => {\n const t = s.type.toLowerCase();\n return t === \"person\" || t === \"animal\";\n });\n if (hasCast) return false;\n const hay = `${scene.summary ?? \"\"} ${scene.start_frame_prompt ?? \"\"} ${scene.end_frame_prompt ?? \"\"} ${scene.action_detail ?? \"\"}`;\n return UI_SURFACE_RE.test(hay);\n}\n\n/**\n * The `screen_still` route: a full-frame SCREEN scene from a REAL screenshot the operator\n * supplies — the faithful, non-garbled reproduction of an app/UI beat. The `[TODO]` names\n * the exact capture command.\n */\nexport function emitScreenScene(ctx: SceneCtx): void {\n emitSurfaceStill(ctx, {\n id: ids.screenRef(ctx.index),\n fallbackLabel: \"the app screen\",\n todo: (label) =>\n `[TODO: supply the real screen for \"${label}\" — never AI-generate a UI. Capture a clean, text-free screenshot with \\`baker images screenshot https://<brand-domain>/<path>\\` (image-library skill); spoken/overlay text rides the overlay layer, not the screenshot. Wire it in as a path relative to this canvas file, never an absolute path]`,\n });\n ctx.out.routes.set(ctx.index, \"screen_still\");\n}\n","import { slotsForFrame } from \"../elements.ts\";\nimport { buildFrameRef } from \"../frames.ts\";\nimport type { SceneCtx } from \"../timeline-env.ts\";\n\n// A scene shorter than this with NO spoken audio is a montage FLASH: a ~1-2s b-roll\n// beat. Seedance's shortest clip is 4s, so generating one (then trimming away ~75%)\n// burns credits for motion no viewer perceives in a flash. We hold the keyframe as a\n// still instead — same look, a fraction of the cost. (Below the Seedance floor of 4s.)\nconst FLASH_HOLD_MAX_S = 2;\n// The raised hold ceiling for a beat the deconstruct EXPLICITLY stamped static/subtle\n// (montage b-roll under a voiceover): just under the Seedance 4s floor, so a 2.5-3.5s\n// near-still beat doesn't bill a 4s clip only to discard most of it in the spine cut.\nconst MONTAGE_HOLD_MAX_S = 3.5;\n\n/**\n * Whether a silent b-roll scene is short and still enough to hold as a keyframe.\n *\n * A flash with REAL subject motion (deconstruct-stamped `dynamic`) keeps a real clip —\n * freezing a pour/spread/hands-working beat turns the montage into a slideshow. Only\n * static/subtle (or unstamped, old-blueprint) flashes hold stills. An EXPLICITLY\n * static/subtle montage beat holds up to the higher MONTAGE_HOLD ceiling: the deconstruct\n * verified its motion is negligible, so a 2.5-3.5s beat billing a 4s clip and discarding\n * most of it buys nothing a held keyframe doesn't. An unstamped scene keeps the\n * conservative 2s ceiling. A scene carrying its own ambience is never held: a still has\n * no sound to extract.\n */\nexport function holdsAsStill(ctx: SceneCtx, ambient: boolean): boolean {\n const { motion_level: motion } = ctx.scene;\n const ceiling = motion === \"static\" || motion === \"subtle\" ? MONTAGE_HOLD_MAX_S : FLASH_HOLD_MAX_S;\n return !ambient && ctx.lengths.dur <= ceiling && motion !== \"dynamic\";\n}\n\n/**\n * The `still_hold` route: a montage FLASH held as a still — one keyframe looped to the\n * scene length on the spine, no billed video_generate.\n */\nexport function emitStillHold(ctx: SceneCtx): void {\n const { scene, index } = ctx;\n const frame = buildFrameRef(ctx, {\n edge: \"start\",\n url: scene.start_frame_asset?.url,\n prompt: scene.start_frame_prompt,\n present: slotsForFrame(ctx.env.slots, index, \"start\"),\n });\n ctx.out.clips.push({ ref: frame, scene_s: ctx.lengths.dur, out: ctx.lengths.out, still: { fit: \"fill\" } });\n ctx.out.routes.set(index, \"still_hold\");\n}\n","import type { VideoBlueprint } from \"@baker/canvas-contract\";\nimport { clipProfileFor, SEEDANCE_PROFILE } from \"../../lib/prompt-profiles.ts\";\nimport type { NodeDecl } from \"../../schema/canvas.ts\";\nimport { voiceLanguageOf } from \"../lib/language.ts\";\nimport type { AudioTrack } from \"./audio.ts\";\nimport { buildPhrases, foldUnshownPhrases, type Phrase } from \"./dialogue/phrases.ts\";\nimport {\n castIdSet,\n collapseVoiceover,\n makePresenterPresent,\n onCameraDialogue,\n sourceShowsPeople,\n} from \"./dialogue/speakers.ts\";\nimport { buildPerSpeakerVoiceConversion, emitPhraseTts, makeVoiceFactory } from \"./dialogue/voices.ts\";\nimport type { ElementSlot } from \"./elements.ts\";\nimport type { ScaffoldVideoOpts } from \"./opts.ts\";\nimport { emitBrandCardScene, sceneIsBrandCard } from \"./routes/brand-card.ts\";\nimport { emitClipScene } from \"./routes/clip.ts\";\nimport {\n compositeVoicing,\n emitCompositeScene,\n type LayeredComposition,\n layeredComposition,\n} from \"./routes/composite.ts\";\nimport { emitGraphicScene, fullFrameGraphicScenes } from \"./routes/graphic-plate.ts\";\nimport { emitPhraseClip, emitSliceScene } from \"./routes/phrase-slice.ts\";\nimport { emitScreenScene, sceneIsFullScreenUi } from \"./routes/screen-still.ts\";\nimport { emitStillHold, holdsAsStill } from \"./routes/still-hold.ts\";\nimport { uiRoutedSceneSet } from \"./routing.ts\";\nimport {\n type SceneCtx,\n type SceneRoute,\n sceneCtx,\n type TalkScene,\n type TimelineEnv,\n type TimelineOut,\n type VoSeg,\n} from \"./timeline-env.ts\";\nimport { clipDurationSet, spineClockFor } from \"./timing.ts\";\n\n/** Build the canvas-wide timeline context, each fact computed once. */\nexport function timelineEnv(args: {\n blueprint: VideoBlueprint;\n slots: ElementSlot[];\n opts: ScaffoldVideoOpts;\n aspect: { outAr: string; genAr: string };\n}): TimelineEnv {\n const { blueprint, slots, opts } = args;\n const durationSet = clipDurationSet(opts.videoModel);\n const ceilingS = Math.max(...durationSet);\n return {\n blueprint,\n clock: spineClockFor(blueprint, ceilingS),\n slots,\n opts,\n outAr: args.aspect.outAr,\n genAr: args.aspect.genAr,\n durationSet,\n ceilingS,\n clipProfile: clipProfileFor(opts.videoModel) ?? SEEDANCE_PROFILE,\n reuse: opts.frames === \"reuse\",\n sourceShowsPeople: sourceShowsPeople(blueprint, slots),\n cameraOn: onCameraDialogue(blueprint),\n casts: castIdSet(blueprint),\n canonical: collapseVoiceover(blueprint),\n ingestCache: new Map<string, string>(),\n uiRouted: uiRoutedSceneSet(blueprint),\n graphicScenes: fullFrameGraphicScenes(blueprint),\n surfaceIngests: new Map<string, string>(),\n ttsLanguageCode: voiceLanguageOf(opts)?.iso,\n spokenLanguageName: voiceLanguageOf(opts)?.name,\n spokenAccent: blueprint.global?.voiceover?.accent?.trim() || undefined,\n keepRenderedVoice: opts.keepRenderedVoice,\n };\n}\n\n/**\n * A b-roll scene (no presenter shown), routed by what its picture IS. Checked in order:\n * - a DESIGNED graphic (meme collage, logo card, marketing art) → the real design asset\n * fit-padded full-frame, never a generated frame (generation garbles designed typography\n * and third-party wordmarks). Checked before the prose screen route: the typed kind is\n * authoritative, and a designed panel must get the design-rebuild wording, not\n * \"screenshot the live page\". Membership is chain-closed: an untyped continuation of a\n * typed graphic take is the same graphic.\n * - a full-frame app/UI screen beat → a REAL screenshot fit-padded full-frame, NEVER a\n * generated frame — before the hold/clip paths, so even a sub-2s screen flash is real.\n * - a solid brand/logo CTA card → a clean colour plate, never a generated logo (the\n * overlay layer's CTA card carries the crisp wordmark + URL).\n * - a short, still flash → a held keyframe.\n * - anything else → its own clip.\n * Returns the scene's END keyframe ref when it is a clip (the next scene may splice from it).\n */\nfunction emitBrollScene(ctx: SceneCtx, prevEndFrame: string | undefined): string | undefined {\n const { env } = ctx;\n const ambient = Boolean(env.opts.ambient) && ctx.mode !== \"ugc_selfie\";\n if (!env.reuse && env.graphicScenes.has(ctx.index)) emitGraphicScene(ctx);\n else if (!env.reuse && sceneIsFullScreenUi(ctx)) emitScreenScene(ctx);\n else if (!env.reuse && sceneIsBrandCard(ctx)) emitBrandCardScene(ctx);\n else if (holdsAsStill(ctx, ambient)) emitStillHold(ctx);\n else return emitClipScene(ctx, prevEndFrame, ambient);\n return undefined;\n}\n\n/**\n * Which phrases get a clip of their own, decided before anything is emitted. A scene is the\n * slice target of at most ONE phrase — if a later phrase shares an already-claimed scene (a\n * within-scene pause), it falls back to a tts read over that scene's clip. `clipScenes` is\n * the scenes a clip phrase slices, and undefined for a read.\n */\nfunction planPhrases(phrases: Phrase[]): Array<{ phrase: Phrase; clipScenes?: number[] }> {\n const claimed = new Set<number>();\n return phrases.map((phrase) => {\n const available = phrase.shownScenes.filter((s) => !claimed.has(s));\n if (!phrase.presenterShown || available.length === 0) return { phrase };\n for (const s of available) claimed.add(s);\n return { phrase, clipScenes: available };\n });\n}\n\n/**\n * Build the whole timeline: the continuous per-phrase voice (presenter clips +\n * converts, or pure-b-roll tts), then the visual spine scene-by-scene — a presenter\n * scene's video is a slice of its phrase clip, a b-roll scene its own silent clip/still,\n * a composite scene its own stacked/overlaid clip. Returns the spine clips, the audio\n * tracks, and the timing plan (`vo_segments`/`talking_scenes`) the validator proves.\n */\nexport function buildTimeline(\n env: TimelineEnv,\n nodes: NodeDecl[],\n): {\n clips: TimelineOut[\"clips\"];\n voTracks: AudioTrack[];\n vo_segments: VoSeg[];\n talking_scenes: TalkScene[];\n routes: Map<number, SceneRoute>;\n} {\n const { blueprint, canonical } = env;\n const composites = new Map<number, LayeredComposition>();\n if (!env.reuse) {\n blueprint.scenes.forEach((s, i) => {\n const composite = layeredComposition(s, env.uiRouted.has(i));\n if (composite) composites.set(i, composite);\n });\n }\n\n // 1. The voice, phrase by phrase (continuous; cut only at pauses).\n const presenterPresent = makePresenterPresent(env.slots, canonical);\n const presentStrict = makePresenterPresent(env.slots, canonical, { strict: true });\n const compositeScenes = new Set(composites.keys());\n const phrases = buildPhrases(blueprint, canonical, compositeScenes, presenterPresent, presentStrict);\n const plan = planPhrases(foldUnshownPhrases(phrases, env.keepRenderedVoice === true));\n // A speaker is cast when a node will read their voice: every read does, and the\n // re-voicing of their on-camera track does unless the rendered voice is kept.\n const readSpeakers = new Set(plan.filter((p) => !p.clipScenes).map((p) => p.phrase.speaker));\n for (const i of compositeScenes) {\n const scene = blueprint.scenes[i];\n const voicing = scene ? compositeVoicing(scene, env) : undefined;\n if (voicing && !voicing.native && voicing.speakers.size >= 2) for (const s of voicing.speakers) readSpeakers.add(s);\n }\n const out: TimelineOut = {\n nodes,\n routes: new Map(),\n clips: [],\n voTracks: [],\n voSegments: [],\n talkingScenes: [],\n nativeSegments: [],\n sceneSlice: new Map(),\n voiceOf: makeVoiceFactory(\n blueprint,\n canonical,\n nodes,\n (speaker) => env.keepRenderedVoice !== true || readSpeakers.has(speaker),\n { language: env.spokenLanguageName, genders: env.opts.voiceGenders },\n env.opts.voiceId,\n ),\n readIds: new Set<string>(),\n };\n plan.forEach(({ phrase, clipScenes }, k) => {\n const voiceNode = out.voiceOf(phrase.speaker);\n if (clipScenes) emitPhraseClip({ ...phrase, shownScenes: clipScenes }, voiceNode, env, out);\n else emitPhraseTts(phrase, voiceNode, k, env, out);\n });\n\n // 2. The visual spine, scene by scene. `prevEndFrame` carries the previous scene's\n // END keyframe ref so a length-split CONTINUATION scene (same physical shot) can\n // reuse it as its START frame — a byte-identical splice, no hard cut. Only the b-roll\n // path produces a shareable single frame; presenter-slice and composite scenes break\n // the chain (they have no single end keyframe), so they reset it.\n let prevEndFrame: string | undefined;\n blueprint.scenes.forEach((_, i) => {\n const composite = composites.get(i);\n const slice = out.sceneSlice.get(i);\n if (composite) {\n emitCompositeScene(env, out, i, composite);\n prevEndFrame = undefined;\n } else if (slice) {\n emitSliceScene(i, slice, out);\n prevEndFrame = undefined;\n } else {\n prevEndFrame = emitBrollScene(sceneCtx(env, out, i), prevEndFrame);\n }\n });\n\n // Every speaker's native audio: one merged convert per speaker. The track length\n // is the SPINE length — the reference duration can include dead air the picture\n // does not reproduce, and `-shortest` at the final mux truncates the longer stream.\n const totalMs = Math.round(env.clock.totalS * 1000);\n out.voTracks.push(...buildPerSpeakerVoiceConversion(out.nativeSegments, totalMs, nodes, env.keepRenderedVoice));\n\n return {\n clips: out.clips,\n voTracks: out.voTracks,\n vo_segments: out.voSegments,\n talking_scenes: out.talkingScenes,\n routes: out.routes,\n };\n}\n","import { MODEL_REGISTRY, type VideoBlueprint } from \"@baker/canvas-contract\";\nimport { sceneRole } from \"../lib/scene-role.ts\";\nimport { canvasDims, parseRatio, type resolveAspect } from \"./aspect.ts\";\nimport type { ScaffoldVideoOpts } from \"./opts.ts\";\nimport type { VideoReport } from \"./report.ts\";\n\n/** The operator-facing `metadata.todo.aspect_remap` note, when the ratios differ. */\nexport function aspectRemapTodo(aspect: ReturnType<typeof resolveAspect>): { aspect_remap?: string } {\n if (!aspect.remapped) return {};\n const dims = canvasDims(aspect.outAr);\n const trims = (parseRatio(aspect.genAr) ?? 0) > (parseRatio(aspect.outAr) ?? 0) ? \"sides\" : \"top/bottom\";\n return {\n aspect_remap:\n `Output is ${aspect.outAr} (${dims.w}×${dims.h}) but the video model generates ${aspect.genAr}; every ` +\n `clip is center-cropped onto the ${aspect.outAr} canvas before the spine. Keep subjects centered in ` +\n `frame prompts — the crop trims the ${trims}.`,\n };\n}\n\n/**\n * A per-curated-video-model constraint summary embedded in `metadata.todo` so an\n * operator swapping a clip's model (e.g. Seedance→Veo to route around the photoreal\n * content-policy block) sees the cross-model deltas inline — the constraints that\n * otherwise only surface after a failed run: aspect must agree across ALL clips,\n * duration is a per-model hard enum, and person_generation differs. `validate` gates\n * all three before any billed render.\n */\nexport function buildModelConstraints(): {\n note: string;\n models: Record<string, { label: string; aspect_ratios: unknown; durations_s: unknown; person_generation: unknown }>;\n} {\n const models: Record<\n string,\n { label: string; aspect_ratios: unknown; durations_s: unknown; person_generation: unknown }\n > = {};\n for (const [id, spec] of Object.entries(MODEL_REGISTRY.video_generate)) {\n const p = spec.params as Record<string, { enum?: readonly unknown[] } | undefined>;\n models[id] = {\n label: spec.label,\n aspect_ratios: p.aspect_ratio?.enum ?? null,\n durations_s: p.duration?.enum ?? null,\n person_generation: p.person_generation?.enum ?? null,\n };\n }\n return {\n note: \"Switching a clip's video model has cross-node constraints. All video_generate nodes in one canvas must share one aspect_ratio (the composite silently crops otherwise). Each model's `duration` is a hard enum and differs per model — a scene longer than the model's max must be split. `person_generation` differs (Veo requires \\\"allow_all\\\"). `baker canvas validate` gates duration, aspect agreement, per-model params, and a scene-span-vs-model-max advisory before any billed run — run it after every model edit.\",\n models,\n };\n}\n\n/**\n * Index of the scene that actually holds the hook — the first scene resolving to\n * role \"hook\", or -1 when none does. The hook is NOT always scene 0: a single-scene\n * video's one scene is the CTA (no hook), and a windowed deconstruct can merge scenes\n * from multiple passes and assign `narrative_role: \"hook\"` to a non-zero index. Every\n * hook instruction derives its `s<i>` references from this so they never point at the\n * wrong scene.\n */\nfunction findHookSceneIndex(blueprint: VideoBlueprint): number {\n const total = blueprint.scenes.length;\n return blueprint.scenes.findIndex((s, i) => sceneRole(s.narrative_role, i, total) === \"hook\");\n}\n\n/**\n * The opener criteria, shared by the per-scene `[RECRAFT HOOK]` flag and the top-level\n * `hook` todo so the thresholds (~2s, ~1s) can't drift between the two prose strings.\n */\nconst HOOK_OPENER_CRITERIA =\n \"statement not question; benefit by ~2s; first frame legible SOUND-OFF in ~1s (the feed plays muted); no bait-and-switch (the hook sets up the body)\";\n\n/**\n * The re-craft instruction is role-aware: the HOOK is the highest-leverage beat\n * (it's targeting, and a competitor's hook often does NOT transfer), so it carries\n * the decision criteria inline and routes to references/hook-craft.md. Other roles\n * keep the generic \"rewrite for OUR brand\" line. Every variant leads with \"[RECRAFT\"\n * so the flag is greppable and never shipped as the reference's words. `sceneIndex`\n * pins the hook variant's `s<i>` references to the scene that actually holds the hook.\n */\nfunction recraftTextForRole(role: string, sceneIndex: number): string {\n if (role === \"hook\") {\n return `[RECRAFT HOOK — the #1 decision, but usually the LIGHTEST move wins: this hook already worked, so default to KEEPING it and building on top (swap only the specifics for ours). REBUILD only when it doesn't transfer — a claim we lack or a different funnel/awareness stage — by reaching for its deeper INNER MECHANIC and delivering that truthfully. DIAGNOSE (device + mechanic + stage) → DECIDE keep/adapt/rebuild → hold the opener to criteria: ${HOOK_OPENER_CRITERIA}. It lives on s${sceneIndex}_start (first frame) + the scene-${sceneIndex} overlay. See references/hook-craft.md + meta-ads-playbook §10/§39.]`;\n }\n if (role === \"cta\") {\n return \"[RECRAFT CTA — the close: state what to do (where to go) + what to expect (offer/bundle/discount) for OUR brand; soft for top-of-funnel, hard for bottom. Don't render the reference's words. See references/script-craft.md.]\";\n }\n return `[RECRAFT: rewrite this ${role} for OUR brand — true claims only; do NOT render the reference's words. See references/script-craft.md + meta-ads-playbook.]`;\n}\n\n/**\n * The per-scene script re-craft checklist (WS11). A video reproduction is\n * INSPIRATION from a winning ad, not a clone: the hook is targeting and doesn't\n * always transfer, so the agent must re-author each scene's line for OUR brand\n * before rendering. We tag each scene's role (DECON `narrative_role`, else\n * inferred from position) and carry the original line FLAGGED so it is never\n * shipped as the competitor's words/claims.\n */\nfunction buildScriptRecraft(blueprint: VideoBlueprint): Array<Record<string, unknown>> {\n const total = blueprint.scenes.length;\n return blueprint.scenes.map((scene, i) => {\n const role = sceneRole(scene.narrative_role, i, total);\n const original = (scene.dialogue ?? [])\n .map((d) => d.line?.trim())\n .filter((l): l is string => Boolean(l))\n .join(\" \");\n return {\n scene: i,\n role,\n original_line: original || null,\n recraft: recraftTextForRole(role, i),\n };\n });\n}\n\n/** Build the structured, in-canvas checklist mirroring the command's stdout report. */\nexport function buildVideoTodo(\n report: VideoReport,\n overlayCount: number,\n floatingCount: number,\n opts: ScaffoldVideoOpts,\n blueprint: VideoBlueprint,\n): Record<string, unknown> {\n // The hook is not always scene 0 (single-scene videos have none; windowed merges can\n // place it at a non-zero index), so derive its index and only emit the `hook` todo when\n // a hook scene actually exists — otherwise it would contradict `script_recraft`'s role.\n const hookSceneIndex = findHookSceneIndex(blueprint);\n const h = hookSceneIndex; // shorthand for the s<i> references below\n return {\n full_flexibility:\n \"THIS CANVAS IS A STARTING POINT, NOT A LOCKED RENDER. It mirrors the reference's structure so you have a faithful scaffold — but you have FULL EDITING FREEDOM and should use it. You can: ADD a scene (new s<i>_start/_end + s<i>_clip, wired into `spine` as the next `c<n>` input with a matching entry in its `clips`), DELETE a scene (drop its nodes, its `spine` input and its `clips` entry), REORDER scenes, SPLIT one beat into two or MERGE two into one, change any frame prompt or motion brief, swap an element reference, re-time or rewrite any overlay/voice, or change a scene's LAYOUT (make a full-frame beat a split-screen/PIP, or flatten a composite to one shot — see `composition`). Re-craft for OUR brand and OUR best ad; the reference is inspiration, not a spec to trace. The content-addressed cache re-bills only what you actually change, so iterate freely. `baker canvas validate` re-checks timing/lip-sync after any edit.\",\n composition:\n \"Some scenes are COMPOSITED, not single shots — `prompt.json`'s scene.composition.layout tells you which: `split_screen` (panels each showing different footage — e.g. b-roll on top, presenter on the bottom), `pip` (a presenter boxed in a corner over full-frame background), or `keyed_overlay` (a green-screen/cut-out presenter over background). Each is reproduced as ONE generated clip PER REGION (`s<i>_r0_*`, `s<i>_r1_*`, …) stacked (vstack/hstack) or overlaid by an `s<i>_composite` ffmpeg node; a keyed presenter runs through `s<i>_key` (video_background_remove) for a transparent cut-out first. Edit each region's own keyframe prompt + motion brief independently. The presenter region (is_presenter) carries the lip-synced voice. To CHANGE a layout, edit composition in prompt.json and re-scaffold, or hand-edit the s<i>_composite ffmpeg args (splitStackArgs/pipOverlayArgs patterns). A clean full-frame talking head is simpler than a composite — flatten when the brand's version doesn't need the split.\",\n recraft_the_script_first: `VIDEO IS INSPIRATION, NOT A CLONE. Before rendering, re-craft the script (hook → body → CTA) for OUR brand — the reference already won in-market, but its hook is targeting and may not transfer.${h >= 0 ? \" The HOOK is the #1 decision (see the `hook` todo);\" : \"\"} ${h >= 0 ? \"then work\" : \"Work\"} the per-scene \\`script_recraft\\` checklist. References: references/hook-craft.md (the hook), references/script-craft.md (body/CTA) + the meta-ads-playbook skill.`,\n ...(h >= 0\n ? {\n hook: `THE HOOK IS THE HIGHEST-LEVERAGE BEAT — the first frame + first 3–4s decide whether the ad is watched at all, and the hook is TARGETING. But highest-leverage does NOT mean always rewrite: this hook already won, so MOST OF THE TIME you KEEP it and build on top (swap only the specifics). REBUILD is the exception — only when it doesn't transfer (a claim we lack or a different funnel/awareness stage), and then by reaching for its deeper INNER MECHANIC and delivering that truthfully, not inventing a new opener from nothing. For scene ${h}: DIAGNOSE it (device + mechanic + what stage it targets), DECIDE keep/adapt/rebuild, then hold the opener to the criteria — ${HOOK_OPENER_CRITERIA}. The hook lives across s${h}_start (the scroll-stopping first frame), the scene-${h} overlay text, the s${h}_clip line, an optional ~0.5s micro-hook, and the ramp into the body. Full diagnose→decide→(keep/adapt/rebuild) discipline + the proven hook-type menu: references/hook-craft.md (+ meta-ads-playbook §10/§17/§39).`,\n }\n : {}),\n script_recraft: buildScriptRecraft(blueprint),\n edit_frames_in_place:\n \"Each s<i>_start keyframe node has its own editable params.prompt (FRAME DESCRIPTION). Edit per frame; prompt.json is the authoritative shared ad spec (cast identity, palette, brand) — its global edits reach every frame automatically because validate/run regenerate the derived prompt.style.json projection each frame reads. Frames are RECAST to the el_* reference images (the source ad's cast is never reused) and are CLEAN PLATES — they render no on-screen text; all text is the overlay HTML layer.\",\n frames_mode: opts.frames ?? \"generate\",\n review_storyboard_before_clips:\n \"STORYBOARD FIRST. Each scene's keyframe (s<i>_start) IS your storyboard — align the LOOK on it before clips bill. Images are cheap, videos aren't, and the content-addressed cache re-bills only changed nodes, so iterate prompts here first and let the storyboard lock before the video_generate clips run. `metadata.video.motion_board` lists each scene's keyframe, window, spoken line, and the graphics scheduled in it — walk it scene by scene.\",\n motion_board:\n \"`metadata.video.motion_board` maps which overlay/graphic fires on which spoken beat (per scene: window_s, spoken line, each graphic's at_s). Review it so every graphic lands on the word it punctuates — don't let an overlay drift off its beat. Adjust an overlay's data-start in video-overlay-composition/index.html (or appears_at_s in prompt.json) to retime it.\",\n assets_required:\n \"MANDATORY: drop a real image at every el_* [TODO] ingest before running — `baker canvas run` refuses to start (and bills nothing) until each placeholder holds a real source.\",\n sourcing:\n 'Resolve each el_* [TODO]: a PERSON needs no slot — they are invented from their description; edit that description (el_* image_generate prompt) to change who they are, or re-scaffold with `--avatar <handle>` when the same actor must recur across ads; never a photo of a real person from the web or the source ad, because the image model reproduces that face. Everything else: the client\\'s OWN assets first — `baker images library \"<subject>\"` and `baker images logo <domain>` for the brand mark; otherwise a FRESH real reference — `baker images find \"<subject>\" --sources pinterest` (photo-real/candid, best for pets, sets and objects) or generate one with the image model. el_location and animals are [SOURCE FRESH]: a DIFFERENT set/animal than the original ad.',\n recurring_elements_to_supply: report.elements,\n text_strategy:\n \"Decide per ad: text is either baked by the generated creative OR painted via the overlay HTML — not both. Default here is clean text-free frames + the HTML overlay layer (video-overlay-composition/index.html) as the single text source, which you fully control.\",\n timeline:\n \"Automatic: each clip is generated at >= its scene length then trimmed back to the real scene duration, so the concatenated picture stays on the same timeline as the audio. You don't manage it.\",\n voices_to_confirm: report.dialogue.map((d) => ({\n scene: d.scene,\n speaker: d.speaker,\n voice_description: d.voice_description,\n line: d.line,\n })),\n talking_head_note:\n \"SHOT-NATIVE: a presenter shot is ONE video clip (its line quoted in s<anchor>_clip's prompt, plus generate_audio on the models that expose it) so lips+voice are generated together — no tts, no veed-lipsync. Two adjacent presenter shots at a hard cut are SEPARATE clips; a cutaway phrase (the presenter on camera, cut to b-roll, back on camera) stays one clip whose on-camera windows are cut inside the spine's per-input chains (no extra nodes). Edit the line in the s<anchor>_clip prompt to re-author it. A pure-voiceover phrase (speaker never shown) is one ElevenLabs tts read instead.\",\n voice_note:\n \"ONE voice per person: a single voice_select is reused across all that person's shots (on-camera AND off — the deconstruct's `voiceover` label folds into the sole presenter). Every presenter clip's native audio is extracted and re-voiced to that brand voice through a SINGLE merged audio_voice_convert per speaker (<voice>_conv, eleven_multilingual_sts_v2, timing preserved so lips stay matched) — so timbre stays consistent across the separate shot clips. Set voice_select.voice_id's gender/language to match the creator.\",\n native_timing:\n \"Clips separate at COMPLETE BREAKS between shots, but the VOICE stays continuous where it should: a voiceover narration is ONE read across the b-roll it plays over, and a cutaway leaves the presenter's read continuous under the insert. Each clip is generated long enough for its estimated speech. `metadata.video.talking_scenes` carries each shot's scene_s vs est_speech_s. CAVEAT: a b-roll cutaway INSIDE a phrase lands at an approximate (proportional) time — no video model exposes word timing — so if a cutaway is off its beat, nudge the scene boundary (it's a starting point). NOTE: a native clip's voice is extracted for the SPOKEN window only (`s<i>_voextract`'s `-t`) — when the scene runs longer than the line, the picture tail is deliberately silent (extracting the full clip would put post-line breathing/babble on the voice bus); trim the scene or extend the line if the dead air reads wrong.\",\n craft: {\n note: \"Production-craft principles that raise every clip's realism. Full rationale: references/video-craft.md (production craft); references/script-craft.md + meta-ads-playbook for the hook/message layer.\",\n principles: [\n \"Iterate cheaply — do NOT brute-force takes. Credits are scarce. Strong inputs (a locked reference + a precise move-by-move prompt) make 1–2 takes land; bad input = bad output. When a take misses, change the SMALLEST thing and re-run — the content-addressed cache re-bills only that node. Images are cheap, videos aren't: fix on the frame/reference, not by re-rolling clips.\",\n \"Keep clips SHORT — the longer a shot holds, the more the eye catches the AI tell. Trust the scene-length trim; don't pad.\",\n \"LOCK THE CAMERA — Seedance animates forward from the single keyframe; only move when the motion brief specifies a move. Unmotivated camera drift is the top realism tell.\",\n \"One generation = one speaking voice. Seedance can't voice two on-camera speakers in one clip — split a two-speaker beat into separate scenes (or one stays silent). Fragment long dialogue at a natural pause and stitch via the shared boundary frame. Lip-sync follows the line verbatim; carry delivery cues in PROSE around the quoted line, never in [brackets] (Seedance renders brackets literally).\",\n \"Preview cheap → finalize high-res (credit discipline). Never prompt the aspect ratio in prose ('make it vertical' → stretch artifacts) — the pipeline sets aspect_ratio as a param.\",\n \"Nail the PACK SHOT — the final product hero sells (motivated camera move, light matched to the rest of the ad).\",\n \"Geometry/objects drifting frame to frame? A MAP beats a paragraph — drop a high-angle schematic still (marked positions) as an extra el_*/reference rather than adding more words.\",\n \"Before/after (dry→wet, skeptic→believer) = two SEPARATE locked reference images — don't ask words to transform a subject mid-scene (cheaper and more reliable than re-rolling clips).\",\n \"The hook is VISUAL-FIRST: the feed plays muted, so the hook scene's opening frame + its overlay text must read sound-off in ~1 second — don't bury the hook in the spoken line alone (meta-ads-playbook §39 visual-hooks-beat-audio, §48 the 1-second/feed-native rule). The hook is also the #1 re-craft decision — diagnose/decide/rebuild it against criteria in references/hook-craft.md (see the `hook` todo).\",\n ],\n },\n transitions:\n \"Scene-to-scene cuts the deconstruct flagged as fade/whip/zoom/dissolve/swipe are reproduced as an ffmpeg xfade at the boundary (everything else stays a hard cut). The overlap is consumed from extra generated footage, so the picture stays exactly on the audio timeline. To change a transition, edit the scene's `transition_out.type` in prompt.json and re-scaffold, or edit that clip's `transition` in the `spine` node's `clips`. For a richer HERO cut (whip-pan, glitch, light-leak, gravitational-lens…), the overlay layer can run a Hyperframes shader/CSS transition instead — see references/hyperframes/blueprints-and-transitions.md (pick 2–3 transition types total; the motion IS the handoff).\",\n overlay_capabilities:\n 'The overlay layer (video-overlay-composition/index.html) is a REAL Hyperframes composition, not a plain text layer — our hyperframe_render node runs the genuine `npx hyperframes` renderer. So it can do far more than fade/slide/pop: (1) PULL a ready-made block — `npx hyperframes catalog --type block` then `npx hyperframes add <id>` into the composition dir, and nest it with a <div data-composition-src=\"compositions/<id>.html\" data-start data-track-index data-width data-height> clip (~154 blocks: lower-thirds, social-proof cards, stat counters, charts, code, logo stings); (2) animate with the named GSAP motion-rule vocabulary (kinetic-beat-slam, svg-path-draw, counting-dynamic-scale, multi-phase-camera…); (3) frame a talking head as a video-call/PIP/split. The whole engine + how to build new effects is documented in references/hyperframes/ (start at README.md → catalog.md, motion-rules.md). Reach for this whenever the reference\\'s graphics are richer than plain text.',\n caption_system:\n \"Sound-off feed = burned-in captions carry the message. The deconstruct produced a word-level transcript — drive a real caption track off it (karaoke highlight → themed → kinetic), not hand-typed text. Floor: `npx hyperframes add caption-highlight` (TikTok karaoke); escalate by content register (caption-kinetic-slam for hype, caption-editorial-emphasis for premium, caption-neon-glow for AI/crypto). Group on meaning, sync within 80ms. Full ladder + transcript contract: references/hyperframes/captions-and-audio.md.\",\n talking_head_overlay:\n \"For a scene where a presenter shows something (screen-share, app demo, product, a 'video call' look), frame the talking-head clip in the overlay composition: presenter full-bleed → shrinks to a rounded corner PIP pill while a content card takes the stage (or split/stack/overlay-glass-card). The pattern, the #video-wrap→corner-pill tween, the glass call card, and the corner-coord presets are in references/hyperframes/talking-head-and-overlays.md. A ready exemplar ships as the `video-call-composition` move — copy it next to your canvas, drop brand fonts in the dir, point a `hyperframe_render` at it with the presenter clip as its `background`.\",\n text_overlays: {\n count: overlayCount,\n note: \"Seeded as editable HTML inside `#overlay-root` in video-overlay-composition/index.html (text + a .pos-* class + data-start/data-dur). PAINT it: restyle the CSS, build lower-thirds/tickers, drop brand-*.otf for on-brand type. The runtime only shows/hides by timestamp.\",\n },\n floating_elements: {\n count: floatingCount,\n note:\n floatingCount > 0\n ? \"Seeded as commented <img> stubs in index.html (each names the `baker images icon/sticker/gif/logo` command to source it) — source the asset, drop it in video-overlay-composition/, uncomment the <img>. For a graphic that should ANIMATE (a lower-third, a social-proof/review card, a stat counter, a chart, a logo sting), prefer a ready-made Hyperframes block instead of a static <img>: `npx hyperframes add <id>` and nest it (see `overlay_capabilities` + references/hyperframes/catalog.md).\"\n : \"none detected by the deconstruct — see `completeness_check`, the reference may still have icons/stickers it missed.\",\n },\n sound_effects: {\n count: report.sfx_count,\n note:\n report.sfx_count > 0\n ? \"Seeded as `sound_effect` nodes on `audio_mix` at their timestamps — edit the prompt or retime.\"\n : \"none detected by the deconstruct — see `completeness_check`, the reference may still have sound cues it missed.\",\n },\n music: {\n present: report.has_music,\n note: report.has_music\n ? \"Original bed regenerated from the deconstruct prompt (styled after the AudD-identified track when available); ducked under the voices.\"\n : \"no music bed scaffolded — if the reference has music, see `completeness_check`.\",\n },\n // ALWAYS-ON safety net: the scaffold can only seed what the deconstruct\n // cataloged, and it under-detects on-image graphics + sound cues. Never trust\n // \"none detected\" — re-watch the reference and fill the gaps with the right tool.\n completeness_check:\n \"The scaffold mirrors the deconstruct's catalog, which UNDER-DETECTS — never trust a 0 count. Re-watch the reference frame-by-frame and add anything missing: \" +\n '(1) ON-IMAGE GRAPHICS not in floating_elements (dollar/coin icons, emojis, checkmarks, rating stars, price tags, arrows, progress bars, app UI) → source each with `baker images icon \"<desc>\"` / `baker images sticker` / `baker images gif` / `baker images logo <domain>` and add it as an <img class=\"ov pos-* \" data-start data-dur> in video-overlay-composition/index.html (never bake graphics into the frame plates). ' +\n \"(2) SOUND CUES not in sound_effects (cha-ching/coin, whoosh, ding, pop, notification, keyboard) → add a `sound_effect` node (eleven_text_to_sound) and wire it onto `audio_mix` at its timestamp. \" +\n \"(3) RECURRING people/animals/products/logos/sets with no el_* slot → add an `ingest` [TODO] slot and reference it from the frames they appear in. \" +\n '(4) Burned-in captions/text not in text_overlays → add an <img>-free <div class=\"ov\"> in index.html. ' +\n \"(5) ONE person playing MULTIPLE personas/wardrobes (skeptic vs believer, before vs after, two outfits) collapsed into a single el_* slot → split into one el_* slot PER look, each linked as the SAME individual via `same_as` so every outfit has its own reference image but the face/identity stays identical.\",\n scenes_clamped_to_clip_max: report.clamped_scenes,\n oversize_scenes:\n report.oversize_scenes.length > 0\n ? {\n scenes: report.oversize_scenes,\n fix: \"Each listed scene's own footage is longer than the gateway-safe ~10s clip ceiling, so its clip will fail the gateway (HTTP 524) — and the scaffold can't auto-split a single scene. Split each into two scenes in prompt.json (scene N+1's start frame = scene N's end frame, so the action stays continuous) before rendering.\",\n }\n : \"none — every scene fits within the gateway-safe clip ceiling.\",\n overstuffed_scenes:\n report.overstuffed_scenes.length > 0\n ? {\n scenes: report.overstuffed_scenes,\n fix: \"Each listed scene's estimated speech runs well past its window — the line will overrun the scene and push the picture off the audio timeline (lips fall behind). Shorten the copy in prompt.json or lengthen the scene before rendering.\",\n }\n : \"none — every scene's spoken length fits its window.\",\n run_warning: \"`baker canvas run` generates many billed image/video/audio assets — validate first, it is not free.\",\n };\n}\n","// Pure transform: a `video-deconstruct/1` blueprint + an AI-selected list of the\n// video's RECURRING identity elements → a runnable `baker-canvas/1` reproduction\n// canvas. No IO — the command layer runs the deconstruct + element-selection\n// passes, bakes the blueprint to `prompt.json`, calls this, validates, and writes.\n//\n// This is the video analogue of `staticAd.ts`, extended for time: every scene\n// boundary is a static-ad-grade `image_generate` (the baked blueprint as\n// `target_blueprint`, a dynamic reference legend, the real extracted frame as a\n// composition anchor), and any recurring element — a person, animal, product, or\n// logo — gets ONE shared `ingest` slot wired into every frame it appears in, so\n// the same actor is grounded by the same source image across the whole video.\n// The per-scene clip (`video_generate`) is fed an ultra-detailed motion brief\n// composed from the scene's action, camera, dialogue, and transcript.\n//\n// This module is the entry: it runs the assembly and names the scaffolder's public\n// surface. Each piece lives in the `video/` module that owns it.\n\nimport { type VideoBlueprint, VideoBlueprintSchema } from \"@baker/canvas-contract\";\nimport type { Canvas, NodeDecl } from \"../schema/canvas.ts\";\nimport { canvasDims, genAspectsFor, resolveAspect } from \"./video/aspect.ts\";\nimport {\n buildSpine,\n emitAudioMix,\n emitBlueprintIngests,\n emitCaptionChain,\n emitFinalMux,\n overlayNode,\n planCaptions,\n} from \"./video/assembly.ts\";\nimport { buildSfxMusic } from \"./video/audio.ts\";\nimport {\n annotateBlueprintWithElements,\n avatarsNotCast,\n buildElementSheets,\n buildElementSlots,\n castAvatarsOnElements,\n emitElementIngests,\n extendPresenceByPromptMentions,\n RecurringElements,\n type RecurringElementT,\n} from \"./video/elements.ts\";\nimport { VIDEO_GUIDE } from \"./video/guide.ts\";\nimport { ids, ref } from \"./video/ids.ts\";\nimport { buildVideoMeta } from \"./video/meta.ts\";\nimport type { ScaffoldVideoOpts } from \"./video/opts.ts\";\nimport { buildOverlayHtml } from \"./video/overlay-html.ts\";\nimport { slimBlueprintForFrameStyle, slimBlueprintForSelection } from \"./video/projections.ts\";\nimport { videoReport } from \"./video/report.ts\";\nimport { buildTimeline, timelineEnv } from \"./video/timeline.ts\";\nimport { buildSpineClock, type SpineClock } from \"./video/timing.ts\";\nimport { aspectRemapTodo, buildModelConstraints, buildVideoTodo } from \"./video/todo.ts\";\n\n// The public surface of the video scaffolder, named from the modules that own each piece.\nexport {\n annotateBlueprintWithElements,\n avatarsNotCast,\n buildOverlayHtml,\n buildSpineClock,\n canvasDims,\n castAvatarsOnElements,\n genAspectsFor,\n resolveAspect,\n type ScaffoldVideoOpts,\n type SpineClock,\n slimBlueprintForFrameStyle,\n slimBlueprintForSelection,\n videoReport,\n};\n\n/**\n * Carry the hook's engineered physical state into its start-frame description so the\n * generator renders it (sweaty/breathless/urgent), not a calm influencer. The\n * deconstruct captures it in `hook_mechanic.why_it_stops_scroll`; without this the\n * blueprint holds the state but the frame prompt never asks for it (CCA-11). Mutates\n * the parsed blueprint in place; idempotent (skips if already injected).\n */\nfunction injectHookPhysicality(blueprint: VideoBlueprint): void {\n for (const scene of blueprint.scenes) {\n const why = scene.hook_mechanic?.why_it_stops_scroll?.trim();\n const prompt = scene.start_frame_prompt?.trim();\n if (!why || !prompt || prompt.includes(why)) continue;\n scene.start_frame_prompt = `${prompt} The subject's physical state IS the scroll-stopper — render it explicitly, not a calm pose: ${why}.`;\n }\n}\n\n/**\n * Why `--frames reuse` cannot apply to this ad, or undefined when it can. Reuse wires the\n * source video's own frames into the clips unchanged, and the source may be somebody\n * else's ad: nothing tells footage the company owns from a competitor's, so an ad with\n * people in it (a deconstructed cast or a recurring person) would ship their faces.\n * Such an ad falls back to generated frames, recast to the `el_*` references.\n */\nfunction framesReuseRefusal(\n blueprint: VideoBlueprint,\n elements: RecurringElementT[],\n requested: ScaffoldVideoOpts[\"frames\"],\n): string | undefined {\n if (requested !== \"reuse\") return undefined;\n const showsPeople =\n (blueprint.global?.cast ?? []).length > 0 || elements.some((e) => e.type.toLowerCase() === \"person\");\n if (!showsPeople) return undefined;\n return \"--frames reuse was not applied: this video shows people, and reusing its frames would put the source's actors into our ad. Every frame is generated instead, recast to the el_* references. Reuse applies only to a video with nobody in it.\";\n}\n\n/**\n * Assemble a full video reproduction canvas. The baked blueprint as the editable\n * `prompt`, one shared `ingest` per recurring element, then the visual spine\n * (per-scene boundary frames → clips → concat), an optional burned-in overlay\n * pass, and an optional audio mix (music + voiceover + SFX) muxed under the video.\n */\nexport function scaffoldVideoCanvas(input: unknown, elementsInput: unknown, requested: ScaffoldVideoOpts): Canvas {\n const blueprint = VideoBlueprintSchema.parse(input);\n injectHookPhysicality(blueprint);\n const elements = RecurringElements.parse(elementsInput);\n const reuseRefused = framesReuseRefusal(blueprint, elements, requested.frames);\n const opts: ScaffoldVideoOpts = reuseRefused ? { ...requested, frames: \"generate\" } : requested;\n // The canvas's two ratios (output, and the one clips generate at) and its one clock,\n // each decided once: every scene, the spine, the mix and the report read the same ones.\n const aspect = resolveAspect(blueprint.source?.aspect_ratio, opts.aspect, genAspectsFor(opts.videoModel));\n const nodes: NodeDecl[] = [];\n\n emitBlueprintIngests(opts, nodes);\n\n // One shared ingest per recurring identity element, wired into every frame it appears in.\n const slots = buildElementSlots(elements);\n // A frame prompt that NAMES an element must also wire it, even when the selection\n // pass missed that scene — a described-but-unwired logo/badge renders wrong-identity.\n extendPresenceByPromptMentions(slots, blueprint);\n emitElementIngests(slots, elements, nodes);\n\n // Fuse each recurring base element (person / animal / product / location) into a\n // multi-view sheet the frames ground on — the single strongest cross-scene consistency\n // lock, so the same face/pet/pack/room is rendered everywhere from ONE multi-angle\n // sheet instead of a lone flat snapshot. Mutates slot.ref + marks slot.sheetBacked\n // before frames read it. (same_as slots reuse their linked slot's identity, no sheet.)\n buildElementSheets(slots, nodes, opts.sheetModel);\n\n // The whole timeline in one pass: the continuous per-phrase voice (presenter clips\n // voice-converted to the brand voice, or pure-b-roll tts) + the scene-by-scene\n // visual spine (presenter scenes sliced from their phrase clip, b-roll/composite on\n // their own). Voice conversion is merged per speaker, so timbre stays continuous even\n // though each shot is its own clip.\n const env = timelineEnv({ blueprint, slots, opts, aspect });\n const { clock } = env;\n const { clips, voTracks, vo_segments, talking_scenes, routes } = buildTimeline(env, nodes);\n\n // Join the per-shot clips into the visual spine (hard cuts, or xfade where the deconstruct\n // detected a transition), deduping the frame a length-split shot shares at its splice.\n // Every input is normalized onto the OUTPUT canvas raster (size/fps/SAR/timebase).\n let videoRef = buildSpine(clips, opts.seamDedup ?? \"head\", canvasDims(aspect.outAr), nodes);\n let videoNode: string = ids.spine;\n\n // Where the burned-in captions' words come from — decided now, because the overlay\n // layer (when there is one) is also what draws them.\n const captions = planCaptions(opts, voTracks);\n\n // Burned-in overlay pass. A UI-routed composite scene contributes no overlay/floating\n // element but still bakes a grouped surface stub into the composition (see\n // uiSurfaceStub), so the overlay layer must be wired for that stub to render once the\n // operator fills it in.\n const overlays = blueprint.scenes.flatMap((s) => s.overlays ?? []);\n const floating = blueprint.scenes.flatMap((s) => s.floating_elements ?? []);\n const overlaid = overlays.length > 0 || floating.length > 0 || env.uiRouted.size > 0;\n if (overlaid) {\n nodes.push(overlayNode(opts, videoRef, captions));\n videoRef = ref(ids.overlaid, \"video\");\n videoNode = ids.overlaid;\n }\n\n // Audio: mix voiceover (built above) + SFX + music onto the timeline. Built BEFORE the\n // caption track (the captions are transcribed from this real mix, below) and muxed under\n // the picture LAST. Voiceover turns are already placed at their real starts.\n const tracks = [...voTracks, ...buildSfxMusic(blueprint, clock, nodes)];\n const audioMixRef = emitAudioMix(tracks, clock, nodes);\n\n // Burned-in word-synced captions (sound-off feed). The caption text is TRANSCRIBED FROM\n // THE REAL GENERATED AUDIO — NOT the deconstruct's original transcript. This is a\n // correctness boundary: wiring the source transcript would burn the COMPETITOR's words\n // (their brand name, a claim we can't make) over our ad once the script is re-authored;\n // transcribing the rendered audio can only ever show what is actually spoken, so it\n // always tracks our re-written lines. On by DEFAULT whenever the command staged the\n // caption composition and there is voice.\n if (captions) {\n const captioned = emitCaptionChain(captions, env, tracks, videoRef, overlaid, nodes);\n videoRef = captioned.videoRef;\n videoNode = captioned.videoNode ?? videoNode;\n }\n\n if (audioMixRef) {\n emitFinalMux(videoRef, audioMixRef, nodes);\n videoNode = ids.final;\n }\n\n const todo = {\n ...buildVideoTodo(\n videoReport(input, elementsInput, opts.videoModel),\n overlays.length,\n floating.length,\n opts,\n blueprint,\n ),\n ...(reuseRefused ? { frames_reuse_refused: reuseRefused } : {}),\n ...aspectRemapTodo(aspect),\n model_constraints: buildModelConstraints(),\n };\n return {\n schema: \"baker-canvas/1\",\n metadata: {\n name: \"video reproduction\",\n description: VIDEO_GUIDE,\n todo,\n // The timing plan `baker canvas validate` checks before any billed render:\n // sequenced voiceover turns (no overlap), audio ≈ video length, and which\n // scenes must be lip-synced.\n video: buildVideoMeta(\n blueprint,\n clock,\n env.ceilingS,\n { vo_segments, talking_scenes, routes },\n slots,\n nodes,\n Boolean(audioMixRef),\n ),\n },\n nodes,\n output: { node: videoNode, output: \"video\" },\n };\n}\n","/**\n * Where a burned-in caption breaks.\n *\n * The karaoke composition sliced the transcript into fixed N-word groups, and a\n * word count knows nothing about the sentence it is cutting. Rendered, that gave\n * cards reading \"OTRA VEZ, EN\" and \"TU CASA GENERA\" — half-thoughts ending on a\n * preposition, which is how a caption track announces that nobody read it.\n *\n * So the break follows the script: a card closes when the line does. The word cap\n * stays as a ceiling, because a fifteen-word clause has to be split somewhere or\n * it renders as a paragraph, and a linking word is never left hanging at the end\n * of a card — \"de\", \"al\", \"y\" carry the sentence into the next word, and a card\n * that ends on one reads as interrupted. Which words those are is decided per script\n * by Jev when the canvas is scaffolded (`carriesOnQuestions`), never listed here: an ad\n * is in any language.\n *\n * Shared with the composition rather than duplicated in it: this is the same rule\n * `caption-style.ts` checks for, and a rule enforced in one place and checked in\n * another is a rule that drifts.\n */\n\nimport type { JevAnswer, JevQuestion } from \"./jev.ts\";\n\n/**\n * One timed word, named as the engine emits it.\n *\n * The field is `text`, not `word` — the video blueprint's transcript words\n * (`transcript_slice`, `@baker/canvas-contract`) define it and every producer follows. Calling it `word` here is what broke the\n * rule below in the composition that renders it: the check read `w.word`, got\n * `undefined` on every real word, and silently fell back to slicing on the word\n * cap — the exact behaviour this module exists to replace.\n */\nexport type CaptionWord = { text: string; start: number; end: number };\n\n/** Terminal marks that end a card. */\nconst CLOSES = /[.,;:!?…]$/;\n\n/** A word as a card's rule compares it: without its closing mark, in lower case. Pure. */\nexport function captionWordKey(text: string): string {\n return text.replace(/[.,;:!?…]+$/, \"\").toLocaleLowerCase();\n}\n\n/**\n * At or above this probability, Jev says the word leans on the next one. Measured: see\n * docs/decision-model.md.\n */\nexport const CARRIES_ON_HELD = 0.4;\n\nconst CARRIES_ON_QUESTION =\n \"Is `word`, as used in `line`, a function word that cannot close a phrase: an article, a preposition, a conjunction, a possessive or another determiner? A noun, verb, adjective, adverb, name or number is not.\";\n\n/**\n * One yes/no question per distinct word of the script, asked in the first line it appears in:\n * whether a card may end on it. Asked of Jev rather than a list of words, because a script is\n * in any language, or two at once, and a list only knows the ones somebody typed. Pure.\n */\nexport function carriesOnQuestions(lines: readonly string[]): { questions: JevQuestion[]; words: string[] } {\n const seen = new Set<string>();\n const questions: JevQuestion[] = [];\n for (const line of lines) {\n for (const text of line.split(/\\s+/)) {\n const word = captionWordKey(text);\n if (!word || seen.has(word)) continue;\n seen.add(word);\n questions.push({ type: \"noul\", instructions: { line, word, question: CARRIES_ON_QUESTION } });\n }\n }\n return { questions, words: [...seen] };\n}\n\n/** The words Jev held as leaning on the next one; none when it could not answer. Pure. */\nexport function carriesOnFromAnswers(words: readonly string[], answers: readonly JevAnswer[] | null): string[] {\n if (!answers) return [];\n return words.filter((_, i) => {\n const answer = answers[i];\n return answer !== undefined && \"noul\" in answer && answer.noul >= CARRIES_ON_HELD;\n });\n}\n\n/**\n * The script's words cut into cards. `carriesOn` holds the words a card may not end on\n * (`captionWordKey` form); empty, the cap alone closes a long clause.\n */\nexport function groupCaptionWords(\n words: CaptionWord[],\n maxPerGroup: number,\n carriesOn: ReadonlySet<string> = new Set(),\n): CaptionWord[][] {\n const cap = Math.max(1, maxPerGroup);\n const groups: CaptionWord[][] = [];\n let current: CaptionWord[] = [];\n\n for (const word of words) {\n current.push(word);\n const last = current[current.length - 1] as CaptionWord;\n // A clause end closes the card outright. The cap only closes it when the card\n // would otherwise run long — and never onto a word that carries on, because\n // that just moves the interrupted thought one word later.\n //\n // So the cap is a PREFERENCE, not a ceiling, and a card can come back a word or two\n // over it: the run of linking words bounds the overshoot, and it is short. That is\n // deliberate and it was measured — \"en la factura de la luz.\" ships as one six-word\n // card at a cap of four, because every earlier cut left a preposition hanging at the\n // end of a card nobody wrote that way. Closing before the linking word instead breaks\n // that same reading: it is the interrupted thought again, one card earlier.\n const closes = CLOSES.test(last.text) || (current.length >= cap && !carriesOn.has(captionWordKey(last.text)));\n if (closes) {\n groups.push(current);\n current = [];\n }\n }\n if (current.length > 0) groups.push(current);\n return groups;\n}\n","/**\n * What a reproduced video's speech says, read when `scaffold-video` builds the canvas: Jev\n * decides, in one request, the language the lines are spoken in (when nobody declared it and the\n * transcriber did not hear it) and the words a caption card may not end on; a text model reads,\n * beside it, the voice each description asks for. Kept in the canvas's rebuild context, so a\n * rebuild never asks again.\n *\n * When a reader cannot answer, nothing is guessed: no language (ElevenLabs detects it from the\n * text), no gender filter (the description alone ranks the cast), the word cap alone.\n */\nimport { carriesOnFromAnswers, carriesOnQuestions } from \"../../nodes/local/lib/caption-groups.ts\";\nimport type { JevAnswer, JevQuestion } from \"../../nodes/local/lib/jev.ts\";\nimport { languageFromAnswer, languageQuestion, spokenLanguage, voiceLanguageOf } from \"../lib/language.ts\";\nimport type { ReadVoiceGenders, VoiceGender } from \"./dialogue/voice-gender.ts\";\nimport type { ScaffoldVideoOpts } from \"./opts.ts\";\n\nexport type SpeechReaders = {\n ask: (questions: readonly JevQuestion[]) => Promise<JevAnswer[] | null>;\n genders: ReadVoiceGenders;\n};\n\nexport type BlueprintSpeech = {\n /** BCP-47 tag of the spoken language: declared, else Jev's when it was sure. */\n spokenLanguage?: string;\n voiceGenders: Record<string, VoiceGender>;\n captionCarriesOn: string[];\n read: boolean;\n};\n\ntype Line = { line?: string; voice_description?: string };\ntype SpeechBlueprint = {\n global?: { cast?: Array<{ description?: string }>; voiceover?: { voice_description?: string } };\n scenes?: Array<{ dialogue?: Line[] }>;\n};\n\nconst asBlueprint = (input: unknown) => (input && typeof input === \"object\" ? (input as SpeechBlueprint) : {});\nconst dialogueOf = (b: SpeechBlueprint): Line[] => (b.scenes ?? []).flatMap((s) => s.dialogue ?? []);\n\n/** Every description a speaker can be cast from (`makeVoiceFactory`), once each, in that order. Pure. */\nexport function blueprintVoices(input: unknown): string[] {\n const b = asBlueprint(input);\n const all = [\n ...dialogueOf(b).map((l) => l.voice_description),\n ...(b.global?.cast ?? []).map((c) => c.description),\n b.global?.voiceover?.voice_description,\n ];\n return [...new Set(all.filter((d): d is string => typeof d === \"string\" && d.length > 0))];\n}\n\n/** Read the blueprint's speech; `declaredLanguage` (a tag) is kept over the lines. */\nexport async function readBlueprintSpeech(\n input: unknown,\n known: { declaredLanguage?: string },\n readers: SpeechReaders,\n): Promise<BlueprintSpeech> {\n const lines = dialogueOf(asBlueprint(input))\n .map((l) => l.line?.trim() ?? \"\")\n .filter(Boolean);\n const declared = known.declaredLanguage ? spokenLanguage(known.declaredLanguage)?.tag : undefined;\n const voices = blueprintVoices(input);\n const carries = carriesOnQuestions(lines);\n const language = !declared && lines.length > 0 ? [languageQuestion(lines)] : [];\n const [answers, genders] = await Promise.all([\n readers.ask([...language, ...carries.questions]),\n readers.genders(voices).catch(() => null),\n ]);\n const voiceGenders: Record<string, VoiceGender> = genders ?? {};\n if (!answers)\n return { ...(declared ? { spokenLanguage: declared } : {}), voiceGenders, captionCarriesOn: [], read: false };\n const read = declared ?? (language.length > 0 ? languageFromAnswer(answers[0]) : undefined);\n return {\n ...(read ? { spokenLanguage: read } : {}),\n voiceGenders,\n captionCarriesOn: carriesOnFromAnswers(carries.words, answers.slice(language.length)),\n read: true,\n };\n}\n\n/**\n * The speech facts a rebuild context written before they were read at scaffold time lacks: the\n * spoken language (unless it names one, as a tag or an older English name) and each voice's\n * gender. Null when the context already has them, or when a reader could not answer — then\n * nothing is guessed and the next rebuild asks again. A context scaffolded since always carries\n * `voiceGenders`, so that field is what marks it as read.\n */\nexport async function speechMissingFromRebuild(\n input: unknown,\n opts: Pick<ScaffoldVideoOpts, \"spokenLanguage\" | \"voiceLanguage\" | \"voiceGenders\">,\n readers: SpeechReaders,\n): Promise<Pick<ScaffoldVideoOpts, \"spokenLanguage\" | \"voiceGenders\"> | null> {\n if (opts.voiceGenders !== undefined) return null;\n const lines = dialogueOf(asBlueprint(input))\n .map((l) => l.line?.trim() ?? \"\")\n .filter(Boolean);\n const voices = blueprintVoices(input);\n if (lines.length === 0 && voices.length === 0) return null;\n const askLanguage = !voiceLanguageOf(opts) && lines.length > 0;\n const [answers, genders] = await Promise.all([\n askLanguage ? readers.ask([languageQuestion(lines)]).catch(() => null) : Promise.resolve([]),\n readers.genders(voices).catch(() => null),\n ]);\n if (!answers || !genders) return null;\n const language = askLanguage ? languageFromAnswer(answers[0]) : undefined;\n return { ...(language ? { spokenLanguage: language } : {}), voiceGenders: genders };\n}\n","import { mkdir, readFile, readdir, rm, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\n/**\n * Per-scene source-of-truth files for a video creative.\n *\n * The video scaffold used to bake the WHOLE deconstruct blueprint — global cast/\n * palette plus a per-scene array that dominates the file (2000+ lines for a 15-scene\n * ad) — into one `prompt.json`. An agent adapting the ad had to edit that monolith\n * (and the equally large canvas), which is too big to edit reliably.\n *\n * Now the blueprint is split: `prompt.json` keeps only the GLOBAL slice, and each\n * scene is written to `scenes/sNN.json`. An agent edits one small scene file at a\n * time; `run`/`validate` re-assemble the full blueprint and re-flow every scene's\n * prompt back into the graph (see `scene-projection.ts`). The scene file is the\n * single source of truth for that scene — the inlined canvas node prompts are a\n * scaffold-time snapshot the sync regenerates.\n */\nexport const SCENES_DIR = \"scenes\";\n/** Global (scene-less) blueprint surface — the file the agent edits for cast/palette/brand. */\nexport const GLOBAL_PROMPT_FILE = \"prompt.json\";\n/**\n * Frozen, non-operator-edited rebuild context ({ elements, opts }) the sync feeds\n * back into `scaffoldVideoCanvas` to regenerate scene prompts deterministically,\n * with NO model call. Written once at scaffold time; never hand-edited.\n */\nexport const REBUILD_FILE = \"prompt.rebuild.json\";\n\n/** `scenes/s00.json`-style name, zero-padded to a stable width so a lexical sort is a scene-order sort. */\nexport function sceneFileName(index: number, total: number): string {\n const width = Math.max(2, String(Math.max(0, total - 1)).length);\n return `s${String(index).padStart(width, \"0\")}.json`;\n}\n\n/** Split an annotated blueprint into its global slice (no scenes) and its scene list. */\nexport function splitBlueprint(blueprint: unknown): { global: Record<string, unknown>; scenes: unknown[] } {\n if (!blueprint || typeof blueprint !== \"object\") {\n return { global: {}, scenes: [] };\n }\n const { scenes, ...global } = blueprint as Record<string, unknown>;\n return { global, scenes: Array.isArray(scenes) ? scenes : [] };\n}\n\n/**\n * Write the split blueprint: `prompt.json` (global only) + `scenes/sNN.json` per\n * scene. Any stale `scenes/*.json` from a previous scaffold with MORE scenes is\n * removed so the directory always mirrors the current blueprint exactly.\n */\nexport async function writeSceneFiles(outDir: string, blueprint: unknown): Promise<void> {\n const { global, scenes } = splitBlueprint(blueprint);\n await writeFile(path.join(outDir, GLOBAL_PROMPT_FILE), `${JSON.stringify(global, null, 2)}\\n`, \"utf8\");\n const scenesDir = path.join(outDir, SCENES_DIR);\n await mkdir(scenesDir, { recursive: true });\n const written = new Set<string>();\n for (let i = 0; i < scenes.length; i++) {\n const name = sceneFileName(i, scenes.length);\n written.add(name);\n await writeFile(path.join(scenesDir, name), `${JSON.stringify(scenes[i], null, 2)}\\n`, \"utf8\");\n }\n for (const name of await listSceneFileNames(scenesDir)) {\n if (!written.has(name)) await rm(path.join(scenesDir, name), { force: true });\n }\n}\n\n/** Scene file names (`sNN.json`) in scene order. Empty when the dir is absent. */\nasync function listSceneFileNames(scenesDir: string): Promise<string[]> {\n let entries: string[];\n try {\n entries = await readdir(scenesDir);\n } catch {\n return [];\n }\n return entries.filter((n) => /^s\\d+\\.json$/.test(n)).sort(bySceneIndex);\n}\n\n/** Absolute scene file paths in scene order for a creative dir. Empty when there is no scenes dir. */\nexport async function listSceneFiles(creativeDir: string): Promise<string[]> {\n const scenesDir = path.join(creativeDir, SCENES_DIR);\n return (await listSceneFileNames(scenesDir)).map((n) => path.join(scenesDir, n));\n}\n\n/**\n * Re-assemble the full blueprint from the global `prompt.json` + `scenes/*.json`.\n * Returns null when the creative has no scene files (an old, unsplit canvas), so\n * the caller can degrade to the legacy single-file behavior.\n */\nexport async function reassembleBlueprint(creativeDir: string): Promise<Record<string, unknown> | null> {\n const files = await listSceneFiles(creativeDir);\n if (files.length === 0) return null;\n const globalRaw = await readFile(path.join(creativeDir, GLOBAL_PROMPT_FILE), \"utf8\");\n const global = JSON.parse(globalRaw) as Record<string, unknown>;\n const scenes: unknown[] = [];\n for (const file of files) {\n scenes.push(JSON.parse(await readFile(file, \"utf8\")));\n }\n return { ...global, scenes };\n}\n\nfunction bySceneIndex(a: string, b: string): number {\n const ai = Number(a.match(/^s(\\d+)\\.json$/)?.[1] ?? 0);\n const bi = Number(b.match(/^s(\\d+)\\.json$/)?.[1] ?? 0);\n return ai - bi;\n}\n","import { readFile, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport {\n AD_IMAGE_MODEL,\n DEFAULT_VIDEO_GENERATE_MODEL,\n isRegisteredModel,\n type NodeKind,\n REFERENCE_SHEET_MODEL,\n} from \"@baker/canvas-contract\";\nimport { type ScaffoldVideoOpts, scaffoldVideoCanvas } from \"../../engine/scaffold/video.ts\";\nimport { type SpeechReaders, speechMissingFromRebuild } from \"../../engine/scaffold/video/speech-reading.ts\";\nimport { listSceneFiles, REBUILD_FILE, reassembleBlueprint } from \"./scene-files.ts\";\n\n/**\n * Re-flow per-scene edits back into the video graph before billing.\n *\n * The scaffold splits the blueprint into `prompt.json` (global) + `scenes/sNN.json`\n * (one per scene). Each scene's generative prompt is ALSO inlined into its\n * `image_generate` / `video_generate` node's `params.prompt` at scaffold time —\n * frozen there, read by the run. So editing a scene file does nothing to the render\n * on its own. This sync closes that gap: it re-assembles the full blueprint from the\n * (edited) scene files, feeds it back through the SAME deterministic builder\n * (`scaffoldVideoCanvas`, no model call) using the frozen rebuild context, and copies\n * the regenerated `params.prompt` onto the matching live nodes. The node's changed\n * param re-keys the content-addressed cache, so only the edited scenes re-bill.\n *\n * It mirrors `syncStyleProjection` (which does the same for global cast/palette via\n * the `prompt_style` projection). It mutates the canvas it is handed; `prepareCanvas`\n * then writes the result back to the canvas file, so the file on disk always carries the\n * prompts that render. The scene files stay the source of truth: an edit to a projected\n * node prompt in the canvas is overwritten on the next preflight.\n *\n * SCOPE: prompt text, and the captions' script (the spoken lines). A timing/structural edit (changed scene window, a scene\n * whose route flips clip↔still, added/removed scenes) is NOT a prompt re-flow — it\n * needs a fresh `baker canvas scaffold-video`. Such edits surface here as a drift\n * warning (a non-prompt param diverged, or the node-id sets don't match), never a\n * silent partial apply.\n */\nconst PROMPT_NODE_TYPES = new Set([\"image_generate\", \"video_generate\"]);\n\ntype CanvasNode = { id?: unknown; type?: unknown; params?: Record<string, unknown> };\n\nexport type SceneSyncResult = \"regenerated\" | \"up_to_date\" | \"not_applicable\";\n\nfunction nodesOfTypes(canvas: unknown, types: ReadonlySet<string>): Map<string, CanvasNode> {\n const out = new Map<string, CanvasNode>();\n const nodes = (canvas as { nodes?: unknown })?.nodes;\n if (!Array.isArray(nodes)) return out;\n for (const n of nodes as CanvasNode[]) {\n if (typeof n?.id === \"string\" && typeof n?.type === \"string\" && types.has(n.type)) {\n out.set(n.id, n);\n }\n }\n return out;\n}\n\nconst promptNodes = (canvas: unknown) => nodesOfTypes(canvas, PROMPT_NODE_TYPES);\n\n/**\n * The captions' `script` is the spoken lines, so a line edited in a scene file has to reach\n * it too. Otherwise the regenerated clip says the new words and the captions, aligned to the\n * old script, print the old ones. Returns how many transcribe nodes changed.\n */\nfunction syncCaptionScripts(canvas: unknown, rebuilt: unknown): number {\n const transcribe = new Set([\"video_transcribe\"]);\n const liveNodes = nodesOfTypes(canvas, transcribe);\n let changed = 0;\n for (const [id, rebuiltNode] of nodesOfTypes(rebuilt, transcribe)) {\n const live = liveNodes.get(id);\n const script = rebuiltNode.params?.script;\n if (!live || typeof script !== \"string\" || live.params?.script === script) continue;\n (live.params ??= {}).script = script;\n changed++;\n }\n return changed;\n}\n\n/** Shallow-equal a node's params EXCEPT `prompt` — a diff here means a structural/timing edit. */\nfunction nonPromptParamsDiverge(live: Record<string, unknown> = {}, rebuilt: Record<string, unknown> = {}): boolean {\n const keys = new Set([...Object.keys(live), ...Object.keys(rebuilt)]);\n keys.delete(\"prompt\");\n for (const k of keys) {\n if (JSON.stringify(live[k]) !== JSON.stringify(rebuilt[k])) return true;\n }\n return false;\n}\n\n/** Each model a rebuild context pins, the node kind it runs as, and the role default that stands in for it. */\nconst REBUILD_MODEL_ROLES: ReadonlyArray<{\n field: \"imageModel\" | \"videoModel\" | \"sheetModel\";\n kind: NodeKind;\n fallback: string;\n}> = [\n { field: \"imageModel\", kind: \"image_generate\", fallback: AD_IMAGE_MODEL },\n { field: \"videoModel\", kind: \"video_generate\", fallback: DEFAULT_VIDEO_GENERATE_MODEL },\n { field: \"sheetModel\", kind: \"image_reference_sheet\", fallback: REFERENCE_SHEET_MODEL },\n];\n\nexport type RebuildModelReplacement = { field: string; from: string; to: string };\n\n/**\n * The rebuild context with every pinned model the registry accepts for its role.\n *\n * A frozen `prompt.rebuild.json` keeps whatever model ids the scaffold wrote, and some\n * are wrong: creatives scaffolded before the sheet model was pinned stored gpt-image\n * as `sheetModel`, which `image_reference_sheet` does not register, so the rebuilt graph\n * would carry an `unknown_model` sheet. Such a model is replaced with its role default\n * and reported, never silently kept. An absent optional model stays absent.\n */\nexport function registeredRebuildOpts(opts: ScaffoldVideoOpts): {\n opts: ScaffoldVideoOpts;\n replaced: RebuildModelReplacement[];\n} {\n const next: ScaffoldVideoOpts = { ...opts };\n const replaced: RebuildModelReplacement[] = [];\n for (const { field, kind, fallback } of REBUILD_MODEL_ROLES) {\n const model = next[field];\n if (model === undefined || isRegisteredModel(kind, model)) continue;\n next[field] = fallback;\n replaced.push({ field, from: model, to: fallback });\n }\n return { opts: next, replaced };\n}\n\n/** One line naming every pinned model the rebuild stood a role default in for, or nothing. */\nfunction warnReplacedModels(replaced: RebuildModelReplacement[], log: (line: string) => void): void {\n if (replaced.length === 0) return;\n const from = replaced.map((r) => `${r.field} \"${r.from}\"`).join(\", \");\n const to = replaced.map((r) => `\"${r.to}\"`).join(\", \");\n log(\n `[scenes] ${REBUILD_FILE} pins ${from}, which the model registry does not accept for that role; ` +\n `rebuilding with ${to} instead. Re-scaffold the creative to record the new model.`,\n );\n}\n\n/** The frozen rebuild context, with its models checked against the registry (see `registeredRebuildOpts`). */\nfunction parseRebuild(raw: string): {\n elements: unknown;\n opts: ScaffoldVideoOpts;\n replaced: RebuildModelReplacement[];\n} {\n const { elements, opts } = JSON.parse(raw) as { elements: unknown; opts: ScaffoldVideoOpts };\n return { elements, ...registeredRebuildOpts(opts) };\n}\n\n/**\n * The rebuild context with the spoken language and each voice's gender, read once for a context\n * written before the scaffold read them and kept in `prompt.rebuild.json`, so every later rebuild\n * reads and casts in them without asking again. Without it, a rebuilt French video read its\n * numbers with English rules and cast its voice with no gender filter. A reader that cannot\n * answer leaves the context as it is.\n */\nasync function withSpeechRead(\n file: string,\n rebuildRaw: string,\n blueprint: unknown,\n opts: ScaffoldVideoOpts,\n readers: SpeechReaders,\n): Promise<ScaffoldVideoOpts> {\n const read = await speechMissingFromRebuild(blueprint, opts, readers);\n if (!read) return opts;\n const stored = JSON.parse(rebuildRaw) as { opts: ScaffoldVideoOpts };\n const kept = { ...stored, opts: { ...stored.opts, ...read } };\n await writeFile(file, `${JSON.stringify(kept, null, 2)}\\n`, \"utf8\").catch(() => undefined);\n return { ...opts, ...read };\n}\n\n/**\n * The canvas nodes whose `params.prompt` the scene files own, each with the scene file it\n * is rebuilt from. Empty for a canvas with no scene files or no rebuild context (its\n * prompts are its own). Throws like `syncSceneNodeParams` on an unreadable scene file.\n */\nexport async function sceneOwnedPrompts(canvasPath: string): Promise<Map<string, string>> {\n const creativeDir = path.dirname(canvasPath);\n const blueprint = await reassembleBlueprint(creativeDir);\n if (!blueprint) return new Map();\n let rebuildRaw: string;\n try {\n rebuildRaw = await readFile(path.join(creativeDir, REBUILD_FILE), \"utf8\");\n } catch {\n return new Map();\n }\n const { elements, opts } = parseRebuild(rebuildRaw);\n const sceneFiles = await listSceneFiles(creativeDir);\n const owned = new Map<string, string>();\n for (const node of scaffoldVideoCanvas(blueprint, elements, opts).nodes) {\n if (!PROMPT_NODE_TYPES.has(node.type)) continue;\n // The video builder tags every frame and clip with the scene it renders, in\n // scene-file order; a node with no scene (an identity sheet) belongs to no file.\n const index = node.tags?.scene;\n const file = index === undefined ? undefined : sceneFiles[index];\n if (file) owned.set(node.id, file);\n }\n return owned;\n}\n\n/**\n * Regenerate scene node prompts from the (possibly edited) scene files and splice them\n * onto `canvas` in place. Call AFTER `resolveRelativeCanvasPaths`, BEFORE billing.\n * `canvasPath` locates the sibling `scenes/` dir and rebuild sidecar. Throws when a\n * scene file / rebuild context is unreadable or the rebuild fails — the caller must\n * fail before billing. Returns `not_applicable` for a canvas with no scene files\n * (a legacy, unsplit creative) so those keep working unchanged.\n */\nexport async function syncSceneNodeParams(\n canvas: unknown,\n canvasPath: string,\n log: (line: string) => void,\n /** Read the speech facts an older rebuild context lacks (`withSpeechRead`); absent, it is rebuilt as it is. */\n readers?: SpeechReaders,\n): Promise<SceneSyncResult> {\n const creativeDir = path.dirname(canvasPath);\n const blueprint = await reassembleBlueprint(creativeDir);\n if (!blueprint) return \"not_applicable\";\n\n let rebuildRaw: string;\n try {\n rebuildRaw = await readFile(path.join(creativeDir, REBUILD_FILE), \"utf8\");\n } catch {\n // No frozen rebuild context. For a canvas with no scene files at all this is an old,\n // unsplit creative and staying quiet is right. But a canvas that HAS scene files and\n // no sidecar is the bad case: someone edits a shot, re-runs, and the edit is\n // discarded without a word. An ad scaffolded before `scaffold-ad` wrote this file\n // behaved exactly that way, and the agent could only report that the frame \"was\n // still cached\" — it was not cached, it was never rebuilt.\n log(\n `[scenes] this creative has scene files but no ${REBUILD_FILE}, so edits to scenes/*.json are NOT reaching ` +\n \"the render — the prompts inlined in the canvas are what renders. Re-scaffold the creative to restore the \" +\n \"link, or edit the canvas node prompts directly.\",\n );\n return \"not_applicable\";\n }\n const parsed = parseRebuild(rebuildRaw);\n const { elements, replaced } = parsed;\n warnReplacedModels(replaced, log);\n const opts = readers\n ? await withSpeechRead(path.join(creativeDir, REBUILD_FILE), rebuildRaw, blueprint, parsed.opts, readers)\n : parsed.opts;\n\n const rebuilt = scaffoldVideoCanvas(blueprint, elements, opts);\n const rebuiltNodes = promptNodes(rebuilt);\n const liveNodes = promptNodes(canvas);\n\n const missingInLive = [...rebuiltNodes.keys()].filter((id) => !liveNodes.has(id));\n const missingInRebuilt = [...liveNodes.keys()].filter((id) => !rebuiltNodes.has(id));\n if (missingInLive.length > 0 || missingInRebuilt.length > 0) {\n log(\n \"[scenes] scene structure changed since scaffold (a scene's route, timing, or count differs) — \" +\n \"re-run `baker canvas scaffold-video` to rebuild the graph. Prompt-only edits were still applied where the nodes still match.\",\n );\n }\n\n let changed = 0;\n let structural = false;\n for (const [id, rebuiltNode] of rebuiltNodes) {\n const live = liveNodes.get(id);\n if (!live) continue;\n const liveParams = (live.params ??= {});\n const rebuiltParams = rebuiltNode.params ?? {};\n if (nonPromptParamsDiverge(liveParams, rebuiltParams)) structural = true;\n if (typeof rebuiltParams.prompt === \"string\" && liveParams.prompt !== rebuiltParams.prompt) {\n liveParams.prompt = rebuiltParams.prompt;\n changed++;\n }\n }\n\n if (structural) {\n log(\n \"[scenes] a scene edit changed clip timing or audio (not just prompt text) — the graph's timing nodes were NOT updated; \" +\n \"re-run `baker canvas scaffold-video` so the spine and voice tracks match.\",\n );\n }\n changed += syncCaptionScripts(canvas, rebuilt);\n if (changed === 0) return \"up_to_date\";\n log(\n `[scenes] re-flowed ${changed} scene prompt(s) from scenes/*.json into the graph — the affected frames/clips will re-bill on the next run`,\n );\n return \"regenerated\";\n}\n","import { readdir, readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { canonicalize } from \"@baker/canvas-contract\";\nimport { parse as parseYaml } from \"yaml\";\nimport { looksLikeHttpUrl } from \"../../engine/lib/url.ts\";\nimport { sha256Hex } from \"../../engine/storage/sha256.ts\";\nimport { listSceneFiles } from \"./scene-files.ts\";\n\n/**\n * THE fingerprint of a creative's editable source: everything an edit can change that\n * changes what the models are sent. It is the one answer to \"was this edited since it last\n * rendered\", and every place that asks uses it:\n *\n * - `baker canvas sync-definition` posts it on every edit (the dashboard's \"current\" half),\n * - a completed `baker canvas run` posts it and records it locally as what it rendered,\n * - `baker canvas stale` (read by the Stop hook and the `validate-creative-renders` check)\n * compares the two,\n * - remote resume adopts an interrupted run only for the same fingerprint.\n *\n * Folded in, length-prefixed so bytes cannot move across a boundary unnoticed:\n * - the graph as it renders: `graph` when the caller has projected it (`prepareCanvas`\n * applies the copy, logo, scene and, later, brief projections, so whatever the models\n * receive from the blueprint lands in the node params hashed here), else\n * `{slug}.canvas.json` as it stands,\n * - its sibling `prompt.json`, without its bookkeeping (`BLUEPRINT_BOOKKEEPING_KEYS`),\n * - every `scenes/sNN.json`, in scene order,\n * - every local composition directory a `hyperframe_render`/`hyperframe_snapshot` node\n * renders (`layout-*`, `_composition-*`, captions), file by file with their paths,\n * - the `formats` list in `_definition.md` (the rest of that file, status and strategy\n * included, is bookkeeping and never changes a render).\n *\n * JSON is hashed in canonical form (sorted keys, no whitespace), so a formatter reflowing a\n * file is not an edit. Content-addressed (never mtime), so a checkout at another path or a\n * clone reads the same. Derived files (`prompt.style.json`, `prompt.plate.json`,\n * `prompt.compose.json`, `prompt.rebuild.json`) are left out: they are rebuilt from the\n * sources above.\n *\n * Prefixed `v2:`. The v1 rule hashed raw bytes; `isSourceStale` (`@baker/api`) never\n * compares fingerprints of different versions, so moving to this rule stales nothing\n * already rendered.\n */\nexport async function computeSourceSha(canvasPath: string, opts: { graph?: unknown } = {}): Promise<string | undefined> {\n let canvasBytes: Buffer;\n try {\n canvasBytes = await readFile(canvasPath);\n } catch {\n // No canvas to fingerprint: the caller degrades (skips staleness), never fails.\n return undefined;\n }\n const canvasDir = path.dirname(canvasPath);\n const parts: Buffer[] = [];\n const fold = (bytes: Buffer) => parts.push(Buffer.from(`${bytes.length}\\0`), bytes);\n\n const graph = opts.graph !== undefined ? opts.graph : parseOrUndefined(canvasBytes);\n fold(graph === undefined ? canvasBytes : Buffer.from(canonicalize(graph)));\n fold(canonicalJson(await readOrEmpty(path.join(canvasDir, \"prompt.json\")), withoutBookkeeping));\n for (const sceneFile of await listSceneFiles(canvasDir)) fold(canonicalJson(await readOrEmpty(sceneFile)));\n for (const dir of compositionDirs(graph, canvasPath)) {\n fold(Buffer.from(`composition:${toPosix(path.relative(canvasDir, dir))}`));\n for (const file of await listCompositionFiles(dir)) {\n fold(Buffer.from(toPosix(path.relative(dir, file))));\n fold(await readOrEmpty(file));\n }\n }\n fold(Buffer.from(JSON.stringify(await definitionFormats(canvasDir))));\n return `${SOURCE_SHA_VERSION}:${sha256Hex(Buffer.concat(parts))}`;\n}\n\nconst SOURCE_SHA_VERSION = \"v2\";\n\nfunction parseOrUndefined(bytes: Buffer): unknown {\n try {\n return JSON.parse(bytes.toString(\"utf8\"));\n } catch {\n return undefined;\n }\n}\n\n/**\n * A JSON source as its canonical form (sorted keys, no whitespace), so a formatter\n * reflowing the file is not an edit. Bytes that do not parse (mid-edit, or empty) count\n * as they are.\n */\nfunction canonicalJson(bytes: Buffer, project: (value: unknown) => unknown = (value) => value): Buffer {\n if (bytes.length === 0) return bytes;\n try {\n return Buffer.from(canonicalize(project(JSON.parse(bytes.toString(\"utf8\")))));\n } catch {\n return bytes;\n }\n}\n\n/**\n * The blueprint's top-level fields that record where the ad came from and why, never what\n * it shows: the winners library's read of the reference (`reference`), the source ad's\n * advertiser and market (`source_context`), and the agent's `notes`. Editing them after a\n * render never makes it stale.\n */\nexport const BLUEPRINT_BOOKKEEPING_KEYS: ReadonlySet<string> = new Set([\"reference\", \"source_context\", \"notes\"]);\n\nfunction withoutBookkeeping(blueprint: unknown): unknown {\n if (!blueprint || typeof blueprint !== \"object\" || Array.isArray(blueprint)) return blueprint;\n return Object.fromEntries(Object.entries(blueprint).filter(([key]) => !BLUEPRINT_BOOKKEEPING_KEYS.has(key)));\n}\n\nasync function readOrEmpty(file: string): Promise<Buffer> {\n try {\n return await readFile(file);\n } catch {\n return Buffer.alloc(0);\n }\n}\n\nconst COMPOSITION_NODE_TYPES = new Set([\"hyperframe_render\", \"hyperframe_snapshot\"]);\n\n/**\n * The local composition directories the canvas renders, resolved against the canvas file,\n * deduped and sorted. A directory holding the canvas itself is skipped: it would fold the\n * whole creative (and every render reference in it) in twice.\n */\nfunction compositionDirs(canvas: unknown, canvasPath: string): string[] {\n const nodes = (canvas as { nodes?: unknown } | null)?.nodes;\n if (!Array.isArray(nodes)) return [];\n const canvasDir = path.dirname(canvasPath);\n const dirs = new Set<string>();\n for (const node of nodes as Array<{ type?: unknown; params?: { composition?: unknown } }>) {\n if (typeof node?.type !== \"string\" || !COMPOSITION_NODE_TYPES.has(node.type)) continue;\n const ref = node.params?.composition;\n if (typeof ref !== \"string\" || ref.length === 0 || ref.includes(\"[TODO\") || looksLikeHttpUrl(ref)) continue;\n const dir = path.resolve(canvasDir, ref);\n const rel = path.relative(dir, canvasDir);\n if (rel === \"\" || (!rel.startsWith(\"..\") && !path.isAbsolute(rel))) continue;\n dirs.add(dir);\n }\n return [...dirs].sort();\n}\n\n// What the renderer itself never copies out of a composition (hyperframe.ts `copyComposition`).\nconst SKIPPED_ENTRIES = new Set([\".cache\", \"node_modules\", \".git\", \".DS_Store\"]);\n\nasync function listCompositionFiles(dir: string): Promise<string[]> {\n let entries: Array<{ name: string; parentPath: string; isFile(): boolean }>;\n try {\n entries = await readdir(dir, { recursive: true, withFileTypes: true });\n } catch {\n return [];\n }\n return entries\n .filter((entry) => entry.isFile())\n .map((entry) => path.join(entry.parentPath, entry.name))\n .filter((file) => !path.relative(dir, file).split(path.sep).some((part) => SKIPPED_ENTRIES.has(part)))\n .sort();\n}\n\n/** The `formats` list from `_definition.md`'s frontmatter, or [] when there is none to read. */\nasync function definitionFormats(canvasDir: string): Promise<unknown[]> {\n let raw: string;\n try {\n raw = await readFile(path.join(canvasDir, \"_definition.md\"), \"utf8\");\n } catch {\n return [];\n }\n const frontmatter = raw.match(/^---\\r?\\n([\\s\\S]*?)\\r?\\n---/)?.[1];\n if (!frontmatter) return [];\n try {\n const formats = (parseYaml(frontmatter) as { formats?: unknown } | null)?.formats;\n return Array.isArray(formats) ? formats : [];\n } catch {\n return [];\n }\n}\n\nfunction toPosix(p: string): string {\n return p.split(path.sep).join(\"/\");\n}\n","/**\n * Jev's answers kept on disk, by the questions and the pinned model. Its probabilities drift by\n * a point between identical calls, so a decision near its threshold could flip from one run to\n * the next and change a prompt that was already paid for; kept, the same spec reads the same.\n */\nimport { createHash } from \"node:crypto\";\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { z } from \"zod\";\nimport { JEV_MODEL, type JevAnswer, type JevQuestion } from \"./jev.ts\";\n\ntype Ask = (questions: readonly JevQuestion[]) => Promise<JevAnswer[] | null>;\n\nconst Kept = z.array(\n z.union([\n z.object({ choice: z.string(), probabilities: z.record(z.string(), z.number()), confidence: z.number() }),\n z.object({ noul: z.number() }),\n ]),\n);\n\n/** `ask`, its answers kept in `<cacheDir>/jev`; an answer that did not come back is never kept. */\nexport function cachedJev(cacheDir: string, ask: Ask): Ask {\n const dir = path.join(cacheDir, \"jev\");\n return async (questions) => {\n const key = createHash(\"sha256\")\n .update(JSON.stringify([JEV_MODEL, questions]))\n .digest(\"hex\");\n const file = path.join(dir, `${key}.json`);\n const kept = Kept.safeParse(\n await readFile(file, \"utf8\")\n .then(JSON.parse, () => null)\n .catch(() => null),\n );\n if (kept.success && kept.data.length === questions.length) return kept.data;\n const answers = await ask(questions);\n if (answers) {\n await mkdir(dir, { recursive: true });\n await writeFile(file, JSON.stringify(answers), \"utf8\");\n }\n return answers;\n };\n}\n","/**\n * Whether a free-text voice description asks for a woman's or a man's voice, so `voice_select`\n * can HARD-filter the cast: its soft scorer alone let a male voice win for a clearly female\n * creator. A description is written in any language (\"Marta, mujer de 40 años\", \"Frau, Mitte\n * 30\", \"若い女性\", \"Narradora cercana\"), so a text model reads it rather than a list of English\n * gender words, which never read the Spanish one and cast a Spanish woman from the English\n * stock voices.\n *\n * A text model, not Jev: this pulls a value out of the words, and the words often carry it in\n * grammar alone. Jev read \"Narradora cercana y cálida\" and \"Locutor profesional\" as not saying,\n * where Gemini Flash-Lite, pinned and at temperature 0, read all 21 labelled descriptions in\n * nine languages right (scripts/ad-spec-reading-evals).\n *\n * Read once, when the canvas is scaffolded, kept in the workspace's cache and in the canvas's\n * rebuild context (`ScaffoldVideoOpts.voiceGenders`), so a rebuild never asks again. With no\n * answer, no description has a gender and the cast is ranked on the description alone.\n */\nimport { createHash } from \"node:crypto\";\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { z } from \"zod\";\nimport { askAboutText, type ReviewUsage } from \"../../../nodes/local/lib/render-review.ts\";\n\nexport type VoiceGender = \"female\" | \"male\";\n\n/** The genders of `voices`, keyed by description, those that say; null when nothing could read them. */\nexport type ReadVoiceGenders = (voices: readonly string[]) => Promise<Record<string, VoiceGender> | null>;\n\n/** Reads, does not judge: a fast model pinned to one provider at temperature 0, so a description reads the same. */\nexport const VOICE_GENDER_MODEL = \"google/gemini-2.5-flash-lite\";\nconst PROMPT_VERSION = 1;\nconst REQUEST = {\n provider: { order: [\"google-vertex\"], allow_fallbacks: false },\n temperature: 0,\n response_format: { type: \"json_object\" },\n};\nconst TIMEOUT_MS = 20_000;\n\n/** What the model is asked: every description at once, one answer each, in order. Pure. */\nexport function voiceGenderPrompt(voices: readonly string[]): string {\n return [\n \"Each line below describes the voice wanted for a speaker in an ad, written in any language.\",\n \"For each, say whether it asks for a woman's voice or a man's voice. Read it in its own language: a noun that is feminine or masculine by itself says so. Answer null when it does not say.\",\n \"\",\n ...voices.map((voice, i) => `${i + 1}. ${JSON.stringify(voice)}`),\n \"\",\n `Reply with only this JSON: {\"genders\": [\"female\" | \"male\" | null, ... one per line, ${voices.length} in order]}`,\n ].join(\"\\n\");\n}\n\nconst Answer = z.object({ genders: z.array(z.enum([\"female\", \"male\"]).nullable()) });\n\n/** The genders the answer gives, keyed by description; null for an answer of another shape or length. Pure. */\nexport function parseVoiceGenders(answer: unknown, voices: readonly string[]): Record<string, VoiceGender> | null {\n const parsed = Answer.safeParse(answer);\n if (!parsed.success || parsed.data.genders.length !== voices.length) return null;\n const out: Record<string, VoiceGender> = {};\n voices.forEach((voice, i) => {\n const gender = parsed.data.genders[i];\n if (gender) out[voice] = gender;\n });\n return out;\n}\n\n/** The real model, each set of descriptions read once and kept in `<cacheDir>/voice-gender`; spend to `onSpend`. */\nexport function voiceGenderModel(cacheDir: string, onSpend: (usage: ReviewUsage) => void): ReadVoiceGenders {\n const dir = path.join(cacheDir, \"voice-gender\");\n return async (voices) => {\n if (voices.length === 0) return {};\n const key = createHash(\"sha256\")\n .update(JSON.stringify([PROMPT_VERSION, VOICE_GENDER_MODEL, voices]))\n .digest(\"hex\");\n const file = path.join(dir, `${key}.json`);\n const kept = parseVoiceGenders(\n await readFile(file, \"utf8\")\n .then(JSON.parse, () => null)\n .catch(() => null),\n voices,\n );\n if (kept) return kept;\n const answer = await askAboutText(VOICE_GENDER_MODEL, voiceGenderPrompt(voices), onSpend, {\n timeoutMs: TIMEOUT_MS,\n request: REQUEST,\n }).catch(() => null);\n const genders = parseVoiceGenders(answer, voices);\n if (genders) {\n await mkdir(dir, { recursive: true });\n await writeFile(file, JSON.stringify(answer), \"utf8\");\n }\n return genders;\n };\n}\n","import { readFile, writeFile } from \"node:fs/promises\";\nimport { slimBlueprintForFrameStyle } from \"../../engine/scaffold/video.ts\";\n\n/**\n * Keep `prompt.style.json` a pure DERIVED artifact of `prompt.json`.\n *\n * Every scaffolded frame reads its shared ad spec via the `prompt_style` ingest —\n * NOT `prompt.json` directly (inlining all scenes into 28 frame prompts is pure\n * waste). The operator is told to edit `prompt.json`; without this sync those edits\n * never reach a single frame, because the projection was written once at scaffold\n * time and nothing regenerated it. Every canvas command's preflight (`prepareCanvas`)\n * calls `syncStyleProjection`, so the projection always reflects the blueprint before\n * anything bills.\n *\n * Cache semantics ride the `ingest` node's mtime:size key: a byte-identical\n * projection is NOT rewritten (no churn on scenes-only edits), and a real style\n * change re-keys the ingest → new asset sha → exactly the affected frames re-bill.\n */\nexport function findBlueprintProjection(canvas: unknown): { promptPath: string; stylePath: string } | null {\n if (!canvas || typeof canvas !== \"object\") return null;\n const nodes = (canvas as { nodes?: unknown }).nodes;\n if (!Array.isArray(nodes)) return null;\n const pathOf = (id: string): string | null => {\n const node = nodes.find((n) => (n as { id?: unknown })?.id === id) as\n | { type?: unknown; params?: { source?: unknown; path?: unknown; expect?: unknown } }\n | undefined;\n if (!node || node.type !== \"ingest\") return null;\n const p = node.params;\n if (p?.source !== \"path\" || p.expect !== \"json\" || typeof p.path !== \"string\" || p.path.includes(\"[TODO\")) {\n return null;\n }\n return p.path;\n };\n const promptPath = pathOf(\"prompt\");\n const stylePath = pathOf(\"prompt_style\");\n return promptPath && stylePath ? { promptPath, stylePath } : null;\n}\n\n/** prompt.json bytes → the slim projection's bytes, in the scaffold's exact format. */\nexport function renderStyleProjection(promptJsonRaw: string): string {\n return renderStyleProjectionFromValue(JSON.parse(promptJsonRaw));\n}\n\n/** The one formatter both the scaffold's initial write and the sync go through. */\nexport function renderStyleProjectionFromValue(blueprint: unknown): string {\n return `${JSON.stringify(slimBlueprintForFrameStyle(blueprint), null, 2)}\\n`;\n}\n\n/**\n * Re-derive prompt.style.json from prompt.json when their contents diverge. Call\n * AFTER `resolveRelativeCanvasPaths` (paths must be absolute). Throws when\n * prompt.json is unreadable or not valid JSON — the caller must fail BEFORE billing.\n */\nexport async function syncStyleProjection(\n canvas: unknown,\n log: (line: string) => void,\n): Promise<\"regenerated\" | \"up_to_date\" | \"not_applicable\"> {\n const pair = findBlueprintProjection(canvas);\n if (!pair) return \"not_applicable\";\n const rendered = renderStyleProjection(await readFile(pair.promptPath, \"utf8\"));\n const current = await readFile(pair.stylePath, \"utf8\").catch(() => null);\n if (current === rendered) return \"up_to_date\";\n await writeFile(pair.stylePath, rendered, \"utf8\");\n log(\n \"[style] prompt.style.json regenerated from prompt.json — every frame's shared ad spec changed; affected image frames will re-bill on the next run\",\n );\n return \"regenerated\";\n}\n","import { readdir, readFile, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { type CanvasPathRewrite, findWorkspaceRoot, healCanvasPaths, normalizeCanvasPaths } from \"./normalize-paths.ts\";\nimport { unsuppliedPlaceholderAssets } from \"./placeholders.ts\";\nimport { interfaceWordsWriter } from \"./interface-words.ts\";\nimport { syncNativeGraph } from \"./native-projection.ts\";\nimport { backendDurableUrlFor, promoteCanvasOutputPaths } from \"./promote-canvas-outputs.ts\";\nimport { localPathParam, resolveRelativeCanvasPaths } from \"./resolve-paths.ts\";\nimport { type SceneSyncResult, syncSceneNodeParams } from \"./scene-projection.ts\";\nimport { claimModels, claimProblems, type RecordPart } from \"./claim-check.ts\";\nimport { computeSourceSha } from \"./source-version.ts\";\nimport { holdUnbilledSpend } from \"./unbilled-spend.ts\";\nimport {\n findBrandRoot,\n readStaticPromptJson,\n syncStaticAdBrief,\n syncStaticAdLogo,\n syncStaticAdReferences,\n} from \"./static-copy-projection.ts\";\nimport { nativeGateProblems } from \"../../engine/scaffold/native-surface.ts\";\nimport { adSpecErrorText, conceptProblems, readAdSpec, sceneWordProblems, unwrittenProblems } from \"../../engine/scaffold/static-ad-spec.ts\";\nimport type { ReviewUsage } from \"../../engine/nodes/local/lib/render-review.ts\";\nimport { askJevQuestions } from \"../../engine/nodes/local/lib/jev.ts\";\nimport { cachedJev } from \"../../engine/nodes/local/lib/jev-cache.ts\";\nimport { voiceGenderModel } from \"../../engine/scaffold/video/dialogue/voice-gender.ts\";\nimport { citedPlatforms, jevCitationJudge } from \"../../engine/scaffold/review-platforms.ts\";\nimport { badgeProblems, jevSignatureJudge, readSignatures, unattributedQuotes } from \"../../engine/scaffold/static-proof.ts\";\nimport { isStaticBriefCanvas } from \"../../engine/scaffold/static-render-briefs.ts\";\nimport { sourcesClaims } from \"../creatives/ideas-model.ts\";\nimport { readInsights } from \"../creatives/ideas-workspace.ts\";\n\ntype SpecGate = {\n code: \"ad_not_written\" | \"scene_words_not_in_copy\" | \"concept_does_not_fit\" | \"proof_without_source\" | \"native_screen_invalid\";\n problems: string[];\n fix: string;\n};\n\n/**\n * What stops a static ad's prompt.json from rendering, all at once under the first category's\n * code: scene words the copy lacks, a concept that does not fit (C1), proof with no source (D3),\n * an invalid native screen. Nothing when it has no readable spec (the brief projection reports that).\n */\nasync function staticSpecGate(canvas: unknown, canvasDir: string): Promise<SpecGate | null> {\n let spec: ReturnType<typeof readAdSpec>;\n try {\n const json = await readStaticPromptJson(canvas);\n if (json === null) return null;\n spec = readAdSpec(json);\n } catch {\n return null;\n }\n const unwritten = unwrittenProblems(spec);\n if (unwritten.length > 0) {\n return {\n code: \"ad_not_written\",\n problems: unwritten,\n fix: \"Write the ad in prompt.json: the scene the viewer sees and each line of copy as it prints. Then re-run.\",\n };\n }\n const concept: SpecGate = {\n code: \"concept_does_not_fit\",\n problems: conceptProblems(spec),\n fix: \"Edit prompt.json's `concept`: name what of ours carries each missing element (`ours`), or pick another reference with `baker winning-ads fit`. Never render a copy that has lost what the reference wins on.\",\n };\n const root = (await findBrandRoot(canvasDir)) ?? findWorkspaceRoot(canvasDir) ?? canvasDir;\n const parts = await readRecordParts(root);\n const records = parts.map((p) => p.text).join(\"\\n\");\n const ours = (await readInsights(root)).filter(sourcesClaims);\n const cacheDir = path.join(findWorkspaceRoot(canvasDir) ?? root, \"canvas\", \".cache\");\n const spent: ReviewUsage[] = [];\n const claims = spec.native\n ? { problems: [], unheld: [] }\n : await claimProblems(\n spec.copy.map((l) => l.text),\n { parts },\n claimModels(cacheDir, (u) => spent.push(u)),\n );\n // Which review platform each line cites for its reviews, read in any language.\n const cited = spec.native\n ? new Map()\n : await citedPlatforms(\n spec.copy.map((l) => l.text),\n jevCitationJudge((u) => spent.push(u)),\n );\n // Which unclear dashes after a customer's words sign them, read before the paid render.\n const signatures = spec.native\n ? undefined\n : await readSignatures(spec, ours, jevSignatureJudge((u) => spent.push(u)));\n // A cent's worth of reading: billed with the workspace's next run, like a translation.\n await holdUnbilledSpend(cacheDir, spent);\n // Every problem in one refusal, each once: one at a time, a producer paid a refusal per problem.\n const gates: SpecGate[] = spec.native\n ? [\n concept,\n {\n // A native screen's people are photos it describes, briefed as ordinary people of the market.\n code: \"native_screen_invalid\",\n problems: nativeGateProblems(spec.native, records, spec.brand?.name),\n fix: \"Fix prompt.json's `native` block.\",\n },\n ]\n : [\n {\n // Words the scene asks for that the copy does not hold: refused here, free, rather than erased by a paid round.\n code: \"scene_words_not_in_copy\",\n problems: sceneWordProblems(spec),\n fix: \"Edit prompt.json: every word or figure the picture shows is a `copy` line (`in_scene`, with `on`); describe any other surface without words.\",\n },\n concept,\n {\n code: \"proof_without_source\",\n problems: [...badgeProblems(spec, cited), ...claims.problems, ...unattributedQuotes(spec, ours, signatures)],\n fix: proofFix(claims.unheld),\n },\n ];\n return oneRefusal(gates.filter((g) => g.problems.length > 0));\n}\n\n/**\n * The proof gate's fix, naming the claims to drop: an agent often reads only a refusal's last\n * lines. Only the company can say a claim is true, so the fix never invites the agent to write\n * one into the records: told to \"source it in the Knowledge\", one wrote it into COMPANY.md. Pure.\n */\nfunction proofFix(claims: readonly string[]): string {\n const named =\n claims.length > 0\n ? `drop ${claims.map((c) => `\"${c}\"`).join(\", \")} (if the user may know it is true, ask them, and record it in the Knowledge only once they confirm it, citing them), and `\n : \"\";\n return `In prompt.json, ${named}wire the badge or drop any other claim named above, everywhere it appears (copy and scene), and sign each customer quote named above with the name given. Never write a claim into the Knowledge or COMPANY.md yourself to pass this check.`;\n}\n\n/** Every refusal at once under the first one's code, each problem once; null when there is none. Pure. */\nfunction oneRefusal(gates: readonly SpecGate[]): SpecGate | null {\n const first = gates[0];\n if (!first) return null;\n return {\n code: first.code,\n problems: [...new Set(gates.flatMap((g) => g.problems))],\n fix: `${gates.map((g) => g.fix).join(\" \")} Then re-run.`,\n };\n}\n\n/**\n * The company's own records a figure must be in: COMPANY.md, every file of src/content but the\n * insights, and the quotes of our own customers as written (a stranger's number is never ours). Never throws.\n */\nasync function readRecordParts(root: string): Promise<RecordPart[]> {\n const parts: RecordPart[] = [\n { source: \"COMPANY.md\", text: await readFile(path.join(root, \"COMPANY.md\"), \"utf8\").catch(() => \"\") },\n ];\n const content = path.join(root, \"src\", \"content\");\n const files = ((await readdir(content, { recursive: true }).catch(() => [])) as string[]).filter(\n (f) => /\\.(md|mdx|json|ya?ml)$/.test(f) && !f.replace(/\\\\/g, \"/\").startsWith(\"insights/\"),\n );\n for (const file of files.slice(0, 400)) {\n parts.push({ source: file, text: await readFile(path.join(content, file), \"utf8\").catch(() => \"\") });\n }\n const quotes = (await readInsights(root)).filter(sourcesClaims).map((insight) => insight.quote);\n if (quotes.length > 0) parts.push({ source: \"customers' words\", text: quotes.join(\"\\n\") });\n return parts.filter((p) => p.text.trim());\n}\nimport { upgradeStaticCanvas } from \"../../engine/scaffold/static-upgrade.ts\";\nimport { syncStyleProjection } from \"./style-projection.ts\";\n\n/**\n * The one preflight every canvas command runs: `validate`, `run`, `critique`,\n * `sync-definition` and `set-prompt` all see the same graph, and (all but the read-only\n * `sync-definition`) leave it in the file.\n *\n * A creative is edited in its blueprint (`prompt.json`, `scenes/sNN.json`), never in the\n * canvas, and several parts of the canvas are projections of it: each scene's frame and\n * clip prompts, a static ad's copy block and type specimen, and the derived style file\n * the frames ingest (`prompt.style.json`). Those projections used to be applied in memory\n * by some commands and not others, so the file on disk, the graph the dashboard drew, the\n * graph the critic scored and the graph that rendered were four different things. Here\n * they are applied once and WRITTEN BACK, so the canvas file says what renders.\n *\n * In order: parse, heal absolute and URL-in-path references (saved), resolve paths against\n * the canvas folder, run every projection, write the projected graph back (relative paths\n * kept, only when it changed), optionally refuse unfilled asset slots, then fingerprint the\n * projected graph.\n *\n * `gate: true` (run, validate) refuses a static ad its prompt.json cannot render honestly: a\n * concept that does not fit, a rating with no source, an invalid native screen.\n *\n * `write: false` is the read-only preview for `sync-definition`, which the file watcher runs\n * over EVERY creative on every edit: it returns the graph that would render (heal, scene and\n * copy projections applied in memory) and touches no file, so an edit to one creative never\n * rewrites another one's files. The derived style file is skipped there: it changes what a\n * frame reads, not the graph.\n */\nexport type PreparedCanvas = {\n /** The graph as it renders: projected, local paths resolved against the canvas folder. */\n canvas: unknown;\n /**\n * The same projected graph in the canvas file's own spelling (canvas-relative paths): what\n * the file says after a write, or would say with `write: false`. What the dashboard is sent.\n */\n fileGraph: unknown;\n /** The canvas file's text as it now stands on disk. */\n text: string;\n /**\n * Fingerprint of the editable source, taken over the projected graph (`fileGraph`), so a\n * read-only preview and a render of the same source agree (see `computeSourceSha`).\n */\n sourceSha: string | undefined;\n /** Whether the projected graph differed from the file and was written to it. */\n written: boolean;\n rewrites: CanvasPathRewrite[];\n projections: {\n style: \"regenerated\" | \"up_to_date\" | \"not_applicable\";\n staticCopy: \"regenerated\" | \"up_to_date\" | \"not_applicable\";\n scenes: SceneSyncResult;\n };\n /** What the projections said (regenerated files, structural drift), for the operator. */\n notes: string[];\n};\n\nexport type CanvasPrepareErrorCode = \"parse\" | \"style_projection\" | \"scene_projection\" | \"unsupplied_assets\" | \"ad_not_written\" | \"scene_words_not_in_copy\" | \"concept_does_not_fit\" | \"proof_without_source\" | \"native_screen_invalid\";\n\n/** A preflight failure: nothing was billed, and the message says what to fix. */\nexport class CanvasPrepareError extends Error {\n readonly code: CanvasPrepareErrorCode;\n readonly fix: string | undefined;\n readonly details: Record<string, unknown> | undefined;\n constructor(code: CanvasPrepareErrorCode, message: string, opts: { fix?: string; details?: Record<string, unknown> } = {}) {\n super(message);\n this.name = \"CanvasPrepareError\";\n this.code = code;\n this.fix = opts.fix;\n this.details = opts.details;\n }\n\n /** The `{ ok: false, error }` envelope every canvas command prints for it. */\n toEnvelope(): { ok: false; error: Record<string, unknown> } {\n return {\n ok: false,\n error: { code: this.code, message: this.message, ...(this.fix ? { fix: this.fix } : {}), ...(this.details ?? {}) },\n };\n }\n}\n\nexport async function prepareCanvas(\n canvasPath: string,\n opts: { requireSuppliedAssets?: boolean; write?: boolean; gate?: boolean; log?: (line: string) => void } = {},\n): Promise<PreparedCanvas> {\n const filePath = path.resolve(canvasPath);\n const canvasDir = path.dirname(filePath);\n const notes: string[] = [];\n const log = (line: string) => {\n notes.push(line);\n opts.log?.(line);\n };\n\n const raw = await readFile(filePath, \"utf8\");\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (e) {\n throw new CanvasPrepareError(\"parse\", `${filePath}: JSON parse error: ${errorMessage(e)}`);\n }\n\n const write = opts.write !== false;\n const healed = write ? await healCanvasPaths(filePath, parsed) : healInMemory(filePath, parsed);\n let onDisk = healed.canvas;\n let text = healed.text ?? raw;\n const rewrites = [...healed.rewrites];\n if (write) {\n const workspaceRoot = findWorkspaceRoot(canvasDir);\n const promoted = workspaceRoot\n ? await promoteCanvasOutputPaths(onDisk, canvasDir, workspaceRoot, backendDurableUrlFor(log), log)\n : { canvas: onDisk, rewrites: [] };\n if (promoted.rewrites.length > 0) {\n onDisk = promoted.canvas;\n text = `${JSON.stringify(onDisk, null, 2)}\\n`;\n await writeFile(filePath, text, \"utf8\");\n rewrites.push(...promoted.rewrites);\n }\n }\n\n // Projections mutate nodes in place and path resolution shares untouched nodes with its\n // input, so they work on a copy: `onDisk` stays the file's own graph to diff against.\n // A static ad scaffolded before the one-picture graph is upgraded first, so everything below\n // (logo, briefs, fingerprint) sees the graph that will render.\n const upgrade = upgradeStaticCanvas(resolveRelativeCanvasPaths(structuredClone(onDisk), canvasDir));\n const canvas = upgrade.canvas;\n if (upgrade.upgraded) {\n log(\"[upgrade] this static ad now makes all its sizes from one picture: one request per variant, a judge, then the pick delivered at the lead size and every other size re-laid out from it (prompt.json, assets and history unchanged)\");\n }\n\n let style: PreparedCanvas[\"projections\"][\"style\"] = \"not_applicable\";\n let staticCopy: PreparedCanvas[\"projections\"][\"staticCopy\"];\n try {\n if (write) style = await syncStyleProjection(canvas, log);\n // A native screen's graph is rebuilt from prompt.json first; the static projections leave it alone.\n // Its interface's words, in a language nobody checked, are written once and billed with the next run.\n const cacheDir = path.join(findWorkspaceRoot(canvasDir) ?? canvasDir, \"canvas\", \".cache\");\n const writing: ReviewUsage[] = [];\n const native = await syncNativeGraph(canvas, log, interfaceWordsWriter(cacheDir, (u) => writing.push(u)));\n await holdUnbilledSpend(cacheDir, writing);\n // The logo first: a dropped logo is unwired before the briefs are compiled without it.\n const logo = native === \"not_applicable\" ? await syncStaticAdLogo(canvas, log) : \"not_applicable\";\n const pictures = native === \"not_applicable\" ? await syncStaticAdReferences(canvas, log) : \"not_applicable\";\n staticCopy = native === \"not_applicable\" ? await syncStaticAdBrief(canvas, log) : native;\n if (logo === \"regenerated\" || pictures === \"regenerated\") staticCopy = \"regenerated\";\n } catch (e) {\n throw new CanvasPrepareError(\"style_projection\", `prompt.json is not readable or not a valid ad:\\n${adSpecErrorText(e)}`, {\n fix: `Fix ${path.join(canvasDir, \"prompt.json\")} so it parses as JSON (and, for a static ad, matches the ad spec the message names), then re-run.`,\n });\n }\n // A concept that does not fit, a rating with no source: refused before anything bills.\n // Only a run or a validate asks (`gate`): critique and set-prompt are advisory, and the read-only\n // preview the dashboard is synced from never stops.\n const gate = opts.gate && isStaticBriefCanvas(canvas) ? await staticSpecGate(canvas, canvasDir) : null;\n if (gate) throw new CanvasPrepareError(gate.code, gate.problems.join(\" \"), { fix: gate.fix });\n let scenes: SceneSyncResult;\n try {\n // A creative scaffolded before its speech was read gets it read once here and kept in its\n // rebuild context; only a writing prepare asks, and its spend waits for the next run record.\n const speechCache = path.join(findWorkspaceRoot(canvasDir) ?? canvasDir, \"canvas\", \".cache\");\n const speechSpent: ReviewUsage[] = [];\n const readers = write\n ? {\n ask: cachedJev(speechCache, (questions) => askJevQuestions(questions, (u) => speechSpent.push(u))),\n genders: voiceGenderModel(speechCache, (u) => speechSpent.push(u)),\n }\n : undefined;\n scenes = await syncSceneNodeParams(canvas, filePath, log, readers);\n await holdUnbilledSpend(speechCache, speechSpent).catch(() => undefined);\n } catch (e) {\n throw new CanvasPrepareError(\n \"scene_projection\",\n `a scenes/*.json file is not readable/valid or its rebuild failed: ${errorMessage(e)}`,\n { fix: `Fix the scene files in ${path.join(canvasDir, \"scenes\")} so each parses as JSON, then re-run.` },\n );\n }\n\n const projected = withFilePaths(canvas, onDisk, canvasDir);\n let written = false;\n if (write && projected !== null && JSON.stringify(projected) !== JSON.stringify(onDisk)) {\n text = `${JSON.stringify(projected, null, 2)}\\n`;\n await writeFile(filePath, text, \"utf8\");\n written = true;\n }\n\n if (opts.requireSuppliedAssets) {\n const pending = unsuppliedPlaceholderAssets(canvas);\n if (pending.length > 0) {\n throw new CanvasPrepareError(\n \"unsupplied_assets\",\n \"This canvas still has placeholder asset slots. Supply a real image (or video) at each before running.\",\n {\n fix: \"Each `el_*` ingest is a [TODO] you fill with the real logo, subject or product (a path relative to the canvas file), then re-run.\",\n details: { assets: pending },\n },\n );\n }\n }\n\n return {\n canvas,\n fileGraph: projected ?? onDisk,\n text,\n sourceSha: await computeSourceSha(filePath, { graph: projected ?? onDisk }),\n written,\n rewrites,\n projections: { style, staticCopy, scenes },\n notes,\n };\n}\n\n/** `healCanvasPaths` without the save: what the file would say once healed. */\nfunction healInMemory(filePath: string, canvas: unknown): { canvas: unknown; rewrites: CanvasPathRewrite[]; text: null } {\n const canvasDir = path.dirname(filePath);\n const workspaceRoot = findWorkspaceRoot(canvasDir);\n if (!workspaceRoot) return { canvas, rewrites: [], text: null };\n return { ...normalizeCanvasPaths(canvas, canvasDir, workspaceRoot), text: null };\n}\n\n/**\n * The projected graph in the file's own spelling: every local path param takes back the\n * value the file had for that node (relative, as the scaffold wrote it), so resolving\n * paths for this process never leaks absolute paths into the committed canvas. A node a\n * projection added (the brand's logo, wired from prompt.json) gets its path relative to the\n * canvas. Null when a node has no id to pair it by: such a canvas fails validation anyway and is\n * left alone.\n */\nfunction withFilePaths(projected: unknown, onDisk: unknown, canvasDir: string): unknown {\n const nodes = (projected as { nodes?: unknown } | null)?.nodes;\n const fileNodes = (onDisk as { nodes?: unknown } | null)?.nodes;\n if (!Array.isArray(nodes) || !Array.isArray(fileNodes)) return null;\n const byId = new Map<string, { params?: Record<string, unknown> }>();\n for (const node of fileNodes as Array<{ id?: unknown; params?: Record<string, unknown> }>) {\n if (typeof node?.id !== \"string\") return null;\n byId.set(node.id, node);\n }\n const out = structuredClone(projected) as { nodes: Array<{ id?: unknown; type?: unknown; params?: Record<string, unknown> }> };\n for (const node of out.nodes) {\n if (typeof node?.id !== \"string\") return null;\n const key = localPathParam(node);\n if (!key || !node.params || typeof node.params[key] !== \"string\") continue;\n const value = node.params[key] as string;\n const original = byId.get(node.id)?.params?.[key];\n // The file keeps its own spelling of a path that still names the same file; a path a\n // projection changed (a reference's new picture) is written relative to the canvas.\n if (typeof original === \"string\" && path.resolve(canvasDir, original) === path.resolve(canvasDir, value)) {\n node.params[key] = original;\n } else if (path.isAbsolute(value)) {\n const rel = path.relative(canvasDir, value).split(path.sep).join(\"/\");\n node.params[key] = rel.startsWith(\".\") ? rel : `./${rel}`;\n }\n }\n return out;\n}\n\n/**\n * Print a preflight failure the way every canvas command reports one and exit 2 (nothing\n * ran, nothing was billed). Anything that is not a preflight failure is rethrown.\n */\nexport function exitOnPrepareError(e: unknown): never {\n if (!(e instanceof CanvasPrepareError)) throw e;\n process.stderr.write(`${JSON.stringify(e.toEnvelope(), null, 2)}\\n`);\n process.exit(2);\n}\n\nfunction errorMessage(e: unknown): string {\n return e instanceof Error ? e.message : String(e);\n}\n","/**\n * Scaffolds (e.g. `scaffold-video`, `scaffold-static-ad`) emit `ingest` nodes whose\n * path is a `[TODO: drop one real source image …]` placeholder — assets the operator\n * must supply before a billed run. Running with them unfilled used to fail deep in\n * the run with a cryptic `file_not_found` AFTER billing had started. This finds them\n * up front so `run` can refuse early with a clear, teaching message.\n */\nexport function unsuppliedPlaceholderAssets(canvas: unknown): Array<{ node: string; placeholder: string }> {\n const nodes = (canvas as { nodes?: unknown })?.nodes;\n if (!Array.isArray(nodes)) return [];\n const out: Array<{ node: string; placeholder: string }> = [];\n for (const n of nodes) {\n const node = n as { id?: unknown; type?: unknown; params?: { source?: unknown; path?: unknown } };\n if (node?.type !== \"ingest\") continue;\n const params = node.params;\n if (params?.source === \"path\" && typeof params.path === \"string\" && params.path.includes(\"[TODO\")) {\n out.push({ node: String(node.id ?? \"?\"), placeholder: params.path });\n }\n }\n return out;\n}\n","/**\n * The words a native screen's own interface prints (WhatsApp's \"Today\", Trustpilot's \"Verified\"),\n * written for a viewer's language that nobody checked against the apps (`native-surface.ts`\n * `needsInterfaceWords`): once per language by the text model the claim gate reads with, pinned to\n * one provider at temperature 0, and kept in the workspace's cache, so every ad in that language\n * prints the same words and the review expects exactly those. A writing that fails is null: the\n * screen speaks English, as it did.\n */\nimport { createHash } from \"node:crypto\";\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { askAboutText, type ReviewUsage } from \"../../engine/nodes/local/lib/render-review.ts\";\nimport {\n INTERFACE_WORDS_PROMPT_VERSION,\n interfaceWordsPrompt,\n readInterfaceWords,\n} from \"../../engine/scaffold/native-surface.ts\";\nimport { CLAIMS_MODEL, CLAIMS_REQUEST } from \"./claim-check.ts\";\nimport type { InterfaceWordsWriter } from \"./native-projection.ts\";\n\nconst WORDS_TIMEOUT_MS = 30_000;\n\n/** The real writer: each language written once and kept in `cacheDir`, the spend of every call made going to `spent`. */\nexport function interfaceWordsWriter(cacheDir: string, spent: (usage: ReviewUsage) => void): InterfaceWordsWriter {\n const dir = path.join(cacheDir, \"interface-words\");\n return async (language) => {\n const tag = language.trim().toLowerCase();\n const key = createHash(\"sha256\")\n .update(JSON.stringify([INTERFACE_WORDS_PROMPT_VERSION, CLAIMS_MODEL, tag]))\n .digest(\"hex\");\n const file = path.join(dir, `${key}.json`);\n const cached = await readFile(file, \"utf8\")\n .then((text) => readInterfaceWords(JSON.parse(text)))\n .catch(() => null);\n if (cached) return cached;\n const words = readInterfaceWords(\n await askAboutText(CLAIMS_MODEL, interfaceWordsPrompt(language), spent, {\n timeoutMs: WORDS_TIMEOUT_MS,\n request: CLAIMS_REQUEST,\n }),\n );\n if (words) {\n await mkdir(dir, { recursive: true });\n await writeFile(file, JSON.stringify(words), \"utf8\");\n }\n return words;\n };\n}\n","/**\n * The claim gate: every claim an ad's copy makes is one the company's records make too, before\n * anything renders. Language is the models' job and equality is code's (`ad-claims.ts`):\n *\n * 1. A text model lists the copy's claims, each as a kind, a number and a unit, in whatever\n * language or languages the ad is written in. A claim the records write in its own words is\n * held (`recordsSay`): two readings of the same words may not agree. An ad with none left is done here.\n * 2. The same model lists the records' claims, in batches, each cached by its text, so a\n * workspace reads its records once until they change.\n * 3. Code holds a figure by a record of the same kind, number and unit.\n * 4. A claim to be the best or the only one is held when the decision model (Jev) says a record\n * makes the same claim: there is no number to compare.\n *\n * When a model cannot answer, the gate degrades and never refuses on a guess: with no reading of\n * the copy, every number it writes in digits must be in the records; with no reading of the\n * records, a figure's number must be; with no judgment, a superlative passes. Measured on\n * 2026-10-07 (scripts/claim-check-evals): see docs/decision-model.md.\n */\nimport { createHash } from \"node:crypto\";\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport {\n CLAIMS_PROMPT_VERSION,\n type Claim,\n claimsPrompt,\n digitsWithoutSource,\n heldBy,\n parseClaims,\n recordsSay,\n recordsWrite,\n} from \"../../engine/scaffold/ad-claims.ts\";\nimport { askJev } from \"../../engine/nodes/local/lib/jev.ts\";\nimport { askAboutText, type ReviewUsage } from \"../../engine/nodes/local/lib/render-review.ts\";\n\n/**\n * Reads claims, it does not judge them: a fast model, pinned to one provider at temperature 0\n * so the same text reads the same. Gemini Flash-Lite answered each ad in about 0.4 s; asked to\n * judge whether a record held a claim, it held \"zwei Tagen\" by \"one day\", which is why it only\n * extracts (docs/decision-model.md § Where it fits in this repo).\n */\nexport const CLAIMS_MODEL = \"google/gemini-2.5-flash-lite\";\nexport const CLAIMS_REQUEST = {\n provider: { order: [\"google-vertex\"], allow_fallbacks: false },\n temperature: 0,\n response_format: { type: \"json_object\" },\n};\nconst CLAIMS_TIMEOUT_MS = 30_000;\n/** At or above this, Jev says a record makes the superlative's claim. Measured: held 0.90–0.96, not held 0.22 or less. */\nexport const SUPERLATIVE_HELD = 0.7;\n/** About 10k tokens of records per reading: small enough that none of its claims is missed. */\nconst RECORDS_BATCH_CHARS = 40_000;\n\nexport type RecordPart = { source: string; text: string };\n\nexport type ClaimModels = {\n /** The claims of `text`, or null when no model could read it. */\n extract: (what: \"ad\" | \"records\", text: string) => Promise<Claim[] | null>;\n /** For each pair, the probability the record makes the claim's claim; null when no judgment came back. */\n judge: (pairs: ReadonlyArray<{ claim: string; record: string }>) => Promise<number[] | null>;\n};\n\n/** A line longer than a reading is read in chunks this much apart less than a reading, so a figure on a cut is whole in one. */\nconst CHUNK_OVERLAP = 200;\n\n/** `pieces` joined with `joiner` into as few runs of at most `RECORDS_BATCH_CHARS` as their order allows. Pure. */\nfunction pack(pieces: readonly string[], joiner: string): string[] {\n const runs: string[] = [];\n let run = \"\";\n for (const piece of pieces) {\n if (run && run.length + joiner.length + piece.length > RECORDS_BATCH_CHARS) {\n runs.push(run);\n run = \"\";\n }\n run = run ? `${run}${joiner}${piece}` : piece;\n }\n return run ? [...runs, run] : runs;\n}\n\n/**\n * `text` in pieces of at most `RECORDS_BATCH_CHARS`, none of it left out: split at its paragraphs, a paragraph\n * still too long at its lines (pretty-printed JSON has no blank line), and a line still too long in overlapping chunks.\n */\nfunction withinReading(text: string, cuts: ReadonlyArray<{ at: RegExp; joiner: string }>): string[] {\n if (text.length <= RECORDS_BATCH_CHARS) return [text];\n const [cut, ...finer] = cuts;\n if (!cut) {\n const chunks: string[] = [];\n for (let at = 0; at < text.length; at += RECORDS_BATCH_CHARS - CHUNK_OVERLAP) {\n chunks.push(text.slice(at, at + RECORDS_BATCH_CHARS));\n if (at + RECORDS_BATCH_CHARS >= text.length) break;\n }\n return chunks;\n }\n return pack(\n text.split(cut.at).flatMap((piece) => withinReading(piece, finer)),\n cut.joiner,\n );\n}\n\nconst CUTS = [\n { at: /\\n\\s*\\n/, joiner: \"\\n\\n\" },\n { at: /\\n/, joiner: \"\\n\" },\n];\n\n/**\n * `text` with each line that says something read once: a line written word for word again (the\n * `\"rating\": 5` of every review) holds no claim the first did not, and a reader asked to list\n * every claim lists it each time, past any answer. Pure.\n */\nfunction saidOnce(text: string): string {\n const seen = new Set<string>();\n return text\n .split(\"\\n\")\n .filter((line) => {\n const said = line.trim();\n if (!/[\\p{L}\\p{N}]/u.test(said)) return true;\n if (seen.has(said)) return false;\n seen.add(said);\n return true;\n })\n .join(\"\\n\");\n}\n\n/** The records in readings of at most `RECORDS_BATCH_CHARS`, in their order, a long part split at its paragraphs. Pure. */\nexport function recordBatches(parts: readonly RecordPart[]): string[] {\n const pieces = parts.flatMap((part) => withinReading(saidOnce(part.text), CUTS));\n return pack(\n pieces.filter((p) => p.trim()),\n \"\\n\\n\",\n );\n}\n\nconst RECORDS_SAY = \"the company's records (src/content, COMPANY.md, its customers' words) do not hold\";\n\n/** The line a claim was read from: the one that holds its words, else the first. */\nfunction lineOf(claim: string, lines: readonly string[]): string {\n const words = claim.toLocaleLowerCase();\n return lines.find((l) => l.toLocaleLowerCase().includes(words)) ?? lines[0] ?? claim;\n}\n\nfunction problemsFor(unheld: readonly string[], lines: readonly string[]): string[] {\n const byLine = new Map<string, string[]>();\n for (const claim of unheld) {\n const line = lineOf(claim, lines);\n byLine.set(line, [...(byLine.get(line) ?? []), claim]);\n }\n return [...byLine].map(([line, claims]) => {\n const named = claims.map((c) => `\"${c}\"`).join(\", \");\n const where = claims.length === 1 && claims[0]?.trim() === line.trim() ? named : `${named} in \"${line}\"`;\n return `${where}: ${RECORDS_SAY} it, so the line promises what nobody wrote. Say only what they record, or drop ${claims.length === 1 ? \"it\" : \"them\"}.`;\n });\n}\n\n/** The claims of `lines` the records do not make, and the refusal's lines for them. Never throws. */\nexport async function claimProblems(\n lines: readonly string[],\n records: { parts: readonly RecordPart[] },\n models: ClaimModels,\n): Promise<{ problems: string[]; unheld: string[] }> {\n const said = lines.filter((l) => l.trim());\n if (said.length === 0) return { problems: [], unheld: [] };\n const text = records.parts.map((p) => p.text).join(\"\\n\\n\");\n const read = await models.extract(\"ad\", said.join(\"\\n\"));\n if (read === null) {\n const unheld = digitsWithoutSource(said, text);\n return { problems: problemsFor(unheld, said), unheld };\n }\n // A claim the records write in the same words is held before any reading is compared.\n const claims = read.filter((c) => !recordsSay(text, c.text));\n if (claims.length === 0) return { problems: [], unheld: [] };\n\n const readings = await Promise.all(recordBatches(records.parts).map((batch) => models.extract(\"records\", batch)));\n const facts = readings.every((r) => r !== null) ? readings.flat() : null;\n const figures = claims.filter((c) => c.kind !== \"superlative\");\n const unheldFigures = figures.filter((c) =>\n facts\n ? !heldBy(c, facts)\n : // No reading of the records: its number, written anywhere in them.\n c.value === null || !recordsWrite(text, String(c.value)),\n );\n\n const superlatives = claims.filter((c) => c.kind === \"superlative\");\n const recorded = (facts ?? []).filter((f) => f.kind === \"superlative\");\n let unheldSuperlatives: Claim[] = [];\n if (superlatives.length > 0 && facts !== null) {\n if (recorded.length === 0) {\n unheldSuperlatives = superlatives;\n } else {\n const pairs = superlatives.flatMap((c) =>\n recorded.map((r) => ({ claim: c.text, record: r.about ? `${r.about}: ${r.text}` : r.text })),\n );\n const judged = await models.judge(pairs);\n if (judged !== null) {\n unheldSuperlatives = superlatives.filter(\n (_, i) => !recorded.some((_, j) => (judged[i * recorded.length + j] ?? 0) >= SUPERLATIVE_HELD),\n );\n }\n }\n }\n const unheld = [...new Set([...unheldFigures, ...unheldSuperlatives].map((c) => c.text))];\n return { problems: problemsFor(unheld, said), unheld };\n}\n\nconst JUDGE_QUESTION =\n \"Does `record` state the claim `claim` makes: the same claim, about the same thing, for the same place or group, in whatever language either is written? A broader claim (a bigger place, best instead of only) is not the same claim.\";\n\n/**\n * The real models: each text's claims read once and kept in `cacheDir` by the text, the model\n * and the prompt, so the same copy and the same records read the same on every run; the spend of\n * every call made goes to `spent`.\n */\nexport function claimModels(cacheDir: string, spent: (usage: ReviewUsage) => void): ClaimModels {\n const dir = path.join(cacheDir, \"claims\");\n return {\n async extract(what, text) {\n const key = createHash(\"sha256\")\n .update(JSON.stringify([CLAIMS_PROMPT_VERSION, CLAIMS_MODEL, what, text]))\n .digest(\"hex\");\n const file = path.join(dir, `${key}.json`);\n const cached = parseClaims(await readFile(file, \"utf8\").then(JSON.parse, () => null).catch(() => null));\n if (cached) return cached;\n const answer = await askAboutText(CLAIMS_MODEL, claimsPrompt(what, text), spent, {\n timeoutMs: CLAIMS_TIMEOUT_MS,\n request: CLAIMS_REQUEST,\n });\n const claims = parseClaims(answer);\n if (claims) {\n await mkdir(dir, { recursive: true });\n await writeFile(file, JSON.stringify({ claims }), \"utf8\");\n }\n return claims;\n },\n judge: (pairs) =>\n askJev(\n pairs.map((p) => ({ instructions: { claim: p.claim, record: p.record, question: JUDGE_QUESTION } })),\n spent,\n ),\n };\n}\n","/**\n * What an ad promises, and whether the company's records promise the same. A model reads the\n * words, in any language or several in one ad (\"en un día\", \"in zwei Tagen\", \"9日で\"), and gives\n * each claim as a kind, a number and a unit; code then compares those exactly, so no list of\n * words decides what a claim is, and no model decides whether two numbers are equal. A claim to\n * be the best or the only one has no number to compare: the decision model judges it\n * (`claim-check.ts`). Pure.\n */\nimport { z } from \"zod\";\nimport { figureForms } from \"./review-platforms.ts\";\n\nexport const CLAIM_KINDS = [\"price\", \"percent\", \"multiple\", \"duration\", \"count\", \"rating\", \"superlative\"] as const;\nconst DURATION_UNITS = [\"minute\", \"hour\", \"day\", \"week\", \"month\", \"year\"] as const;\n\nexport const Claim = z.object({\n /** The claim as it is written, in its own language. */\n text: z.string().min(1),\n kind: z.enum(CLAIM_KINDS),\n /** The number it states, as a number; null for a superlative. */\n value: z.number().nullable().catch(null),\n /** A duration's unit, a price's ISO currency, what a count or rating counts (in English); else null. */\n unit: z.string().nullable().catch(null),\n /** What it is about, in a few English words: what the decision model compares a superlative on. */\n about: z.string().catch(\"\"),\n});\nexport type Claim = z.infer<typeof Claim>;\n\nconst Answer = z.object({ claims: z.array(z.unknown()) });\n\n/** Bump when the prompt changes what a claim is: cached answers of the old prompt are not reused. */\nexport const CLAIMS_PROMPT_VERSION = 1;\n\n/** What the model is asked about `text` (an ad's copy, or a part of the records): every claim, normalised. Pure. */\nexport function claimsPrompt(what: \"ad\" | \"records\", text: string): string {\n const label = what === \"ad\" ? \"the copy of an ad, one line per line\" : \"a part of a company's own records\";\n return [\n `Below is ${label}, in any language or several. List every claim in it: a specific price, percentage, multiple, duration, count or rating (written in digits or in words), or a claim to be the best, the only, the first, the fastest, number one or a leader. Plain words with no number and no such claim are not claims: \"Get a quote\" and \"free\" are not.`,\n \"\",\n \"For each claim give:\",\n '- \"text\": the claim exactly as written there',\n `- \"kind\": ${CLAIM_KINDS.join(\" | \")}`,\n '- \"value\": the number as a plain number (one day → 1, \"4,8\" → 4.8, \"1.200 reviews\" → 1200, half the bill → kind percent, 50), or null for a superlative',\n `- \"unit\": for a duration one of ${DURATION_UNITS.join(\" | \")}; for a price its ISO currency; for a count or rating what is counted or rated, in English (reviews, stars, homes); else null`,\n '- \"about\": what the claim is about, in a few English words (install time, warranty, bill savings, Trustpilot rating, battery recycling in Valencia)',\n \"\",\n \"Text:\",\n '\"\"\"',\n text,\n '\"\"\"',\n \"\",\n 'Reply with only this JSON: {\"claims\": [{\"text\": \"…\", \"kind\": \"…\", \"value\": …, \"unit\": …, \"about\": \"…\"}]}',\n ].join(\"\\n\");\n}\n\n/** The claims of a model's answer, each one it gave in the shape asked; null when the answer is not that shape at all. Pure. */\nexport function parseClaims(answer: unknown): Claim[] | null {\n const parsed = Answer.safeParse(answer);\n if (!parsed.success) return null;\n return parsed.data.claims.flatMap((c) => {\n const claim = Claim.safeParse(c);\n return claim.success ? [claim.data] : [];\n });\n}\n\nconst singular = (unit: string | null) => (unit ?? \"\").trim().toLowerCase().replace(/s$/, \"\");\n\nfunction sameNumber(a: number | null, b: number | null): boolean {\n return a !== null && b !== null && Math.abs(a - b) <= 1e-9 * Math.max(1, Math.abs(a));\n}\n\n/**\n * The claim is one of `facts`: the same kind and number, and for a duration, a price or a count\n * the same unit (a share, a multiple or a rating is its number). A fact read with no number holds\n * the number its own words write. A superlative is never held\n * here: whether two are the same claim is a judgment, not an equality. Pure.\n */\nexport function heldBy(claim: Claim, facts: readonly Claim[]): boolean {\n if (claim.kind === \"superlative\") return false;\n return facts.some((fact) => {\n const sameFigure =\n sameNumber(fact.value, claim.value) ||\n (fact.value === null && claim.value !== null && recordsWrite(fact.text, String(claim.value)));\n if (fact.kind !== claim.kind || !sameFigure) return false;\n if (claim.kind === \"duration\" || claim.kind === \"price\" || claim.kind === \"count\") {\n return !fact.unit || !claim.unit || singular(fact.unit) === singular(claim.unit);\n }\n return true;\n });\n}\n\n/** Words as compared literally: accents and case aside, punctuation as space. */\nconst literalWords = (text: string) =>\n text\n .normalize(\"NFKD\")\n .replace(/\\p{M}/gu, \"\")\n .toLocaleLowerCase()\n .replace(/[^\\p{L}\\p{N}]+/gu, \" \")\n .trim();\n\n/**\n * The records write the claim in its own words (\"zero mess\", \"halved\"), accents, case and punctuation\n * aside: held whatever kind or number either reading gave it, as a customer's quote recorded word\n * for word holds every claim in it. Code equality, in any language. Pure.\n */\nexport function recordsSay(records: string, claim: string): boolean {\n const words = literalWords(claim);\n return words !== \"\" && ` ${literalWords(records)} `.includes(` ${words} `);\n}\n\nconst escapeRegExp = (text: string) => text.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n\n/** `records` write `figure` as a number of its own (not inside \"19\" or \"4.9\"), either decimal mark alike. Pure. */\nexport function recordsWrite(records: string, figure: string): boolean {\n return figureForms(figure).some((form) =>\n new RegExp(`(?<![\\\\d.,])${escapeRegExp(form)}(?![\\\\d]|[.,]\\\\d)`).test(records),\n );\n}\n\n/** A number written in digits, what every language and script shares, with the word written right after it (\"15 minutes\", \"30%\"). */\nconst FIGURE = /(\\d+(?:[.,]\\d+)*)(?:\\s?[\\p{L}%]+)?/gu;\n\n/**\n * When no model can read the copy, the one check no language can fool: every number the copy\n * writes in digits is one the records write too (\"4,8\" and \"4.8\" alike). Each figure that is\n * not, once, with the word it is written beside, so the agent knows which claim to drop. Pure.\n */\nexport function digitsWithoutSource(lines: readonly string[], records: string): string[] {\n const unheld = lines.flatMap((line) =>\n [...line.matchAll(FIGURE)].filter((m) => !recordsWrite(records, m[1] ?? \"\")).map((m) => m[0].trim()),\n );\n return [...new Set(unheld)];\n}\n","/**\n * A static ad whose prompt.json holds a `native` block is drawn as that screen (`static-native-graph.ts`):\n * on every run its render graph is rebuilt from prompt.json, so adding the block to an existing ad\n * switches it to the native route, and a photo added, changed or removed in the spec is the only\n * thing that renders again. Runs before the static projections, which leave a native canvas alone.\n */\nimport path from \"node:path\";\nimport { pickImageModel } from \"../../engine/lib/image-model-router.ts\";\nimport { type InterfaceWords, needsInterfaceWords } from \"../../engine/scaffold/native-surface.ts\";\nimport { readAdSpec } from \"../../engine/scaffold/static-ad-spec.ts\";\nimport { readBrandLogoFiles, readStaticBrandKit } from \"../../engine/scaffold/static-brand.ts\";\nimport { deliveryOf, isNativeCanvas, nativeCanvas } from \"../../engine/scaffold/static-native-graph.ts\";\nimport { STATIC_AD_ROLE } from \"../../engine/scaffold/static-render-briefs.ts\";\nimport { renderParamsFor } from \"../../engine/scaffold/staticAd.ts\";\nimport { findBrandRoot, readStaticPromptJson } from \"./static-copy-projection.ts\";\n\nexport type NativeSyncResult = \"not_applicable\" | \"up_to_date\" | \"regenerated\";\n\ntype Holder = { nodes?: unknown };\n\nfunction promptPathOf(canvas: unknown): string | null {\n const nodes = (canvas as Holder | null)?.nodes;\n if (!Array.isArray(nodes)) return null;\n const prompt = nodes.find(\n (n: { id?: unknown; type?: unknown; tags?: { role?: unknown } }) =>\n n?.id === \"prompt\" && n.type === \"ingest\" && n.tags?.role === STATIC_AD_ROLE,\n ) as { params?: { path?: unknown } } | undefined;\n return typeof prompt?.params?.path === \"string\" ? prompt.params.path : null;\n}\n\n/** Writes the interface's words for a language (`interface-words.ts`), or null when no model could. */\nexport type InterfaceWordsWriter = (language: string) => Promise<InterfaceWords | null>;\n\n/**\n * Rebuild a native static ad's graph from its prompt.json; leave every other canvas as it is. A\n * viewer's language with no checked interface words has them written once (`writeWords`) and handed\n * to every screen, so the page and the review print the same; with none, the screen speaks English.\n */\nexport async function syncNativeGraph(\n canvas: unknown,\n log: (line: string) => void,\n writeWords?: InterfaceWordsWriter,\n): Promise<NativeSyncResult> {\n const source = promptPathOf(canvas);\n if (!source) return \"not_applicable\";\n const raw = await readStaticPromptJson(canvas);\n if (!raw) return \"not_applicable\";\n const spec = readAdSpec(raw);\n if (!spec.native) return \"not_applicable\";\n const was = isNativeCanvas(canvas);\n const delivery = deliveryOf(canvas);\n const genModel = delivery.genModel ?? pickImageModel(\"photo_people\", \"generate\");\n const root = await findBrandRoot(path.dirname(source));\n const name = spec.brand?.name ?? (root ? (await readStaticBrandKit(root)).name : undefined);\n const files = root ? await readBrandLogoFiles(root, name) : { logos: [] };\n const holder = canvas as Holder & { nodes: unknown[] };\n const before = JSON.stringify(holder.nodes);\n const fontRef = holder.nodes.some((n) => (n as { id?: unknown }).id === \"font_headline\")\n ? \"$ref:font_headline.asset\"\n : undefined;\n const language = spec.viewer?.market?.language;\n const interfaceWords =\n language && writeWords && needsInterfaceWords(language) ? await writeWords(language).catch(() => null) : null;\n const next = nativeCanvas(holder, spec, {\n ...(interfaceWords ? { interfaceWords } : {}),\n genModel,\n genParams: Object.keys(delivery.genParams).length > 0 ? delivery.genParams : renderParamsFor(genModel),\n formats: delivery.formats.length > 0 ? delivery.formats : (spec.formats ?? [\"4:5\"]),\n ...(delivery.placements ? { placements: delivery.placements } : {}),\n logos: spec.logo || spec.native.frame.logo || usesLogo(spec) ? files.logos : [],\n ...(files.mark && usesLogo(spec) ? { mark: files.mark } : {}),\n ...(fontRef ? { fontRef } : {}),\n resolve: (image) =>\n /^https?:\\/\\//i.test(image)\n ? { source: \"url\", url: image }\n : { source: \"path\", path: path.resolve(path.dirname(source), image) },\n });\n holder.nodes = next.nodes;\n if (JSON.stringify(holder.nodes) === before) return \"up_to_date\";\n log(\n was\n ? \"[native] prompt.json's screen changed: the photos it shows are rendered again only where their description changed\"\n : `[native] prompt.json draws this ad as a ${spec.native.surface.replace(\"_\", \" \")} screen: every size is drawn exactly from it, and the image model renders only the photos it holds`,\n );\n return \"regenerated\";\n}\n\n/** Whether the screen shows the brand's logo anywhere (an avatar, the review site's summary, the frame). */\nfunction usesLogo(spec: ReturnType<typeof readAdSpec>): boolean {\n const native = spec.native;\n if (!native) return false;\n return native.frame.logo || JSON.stringify(native).includes('\"avatar\":\"logo\"') || \"summary\" in native;\n}\n","import { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { BackendClient } from \"../../engine/client/backend-client.ts\";\nimport { optionalConnection } from \"../../env.ts\";\nimport { canvasAssetMime, inferMimeFromPath } from \"../../engine/nodes/ingest.ts\";\nimport type { AssetKind } from \"../../engine/schema/refs.ts\";\nimport { sha256Hex } from \"../../engine/storage/sha256.ts\";\nimport type { CanvasPathRewrite } from \"./normalize-paths.ts\";\n\n/**\n * The durable URL of the asset with this sha, or null when the store does not hold it.\n * Given `bytes`, it keeps them first, so the answer is never null for a file in hand.\n */\nexport type DurableUrlFor = (args: { sha256: string; mime: string; bytes?: Buffer }) => Promise<string | null>;\n\n/**\n * Point every `ingest` that reads from `canvas/` at the durable copy of what it reads.\n *\n * `canvas/` is the engine's own working folder — the content-addressed cache\n * (`canvas/.cache/assets/<xx>/<sha>.<ext>`) and each run's outputs (`canvas/r_<id>/…`) —\n * and it is gitignored. A canvas that reads from it renders in the Session that wrote it\n * and nowhere else: the commit gate sees the file and passes, then every later Session,\n * which starts without that folder, fails its publish on a creative nobody is looking at.\n * That is what happened to four published UGC ads whose voice takes were wired from the\n * cache after a download failed.\n *\n * There is nothing to ask the author: the bytes are already in our store under their sha\n * (or are kept now, from the file in hand), and an ingest of that URL returns the same\n * asset with the same sha, so nothing downstream re-renders. A cache path names its sha in\n * its own filename, so it heals even after the cache is gone. Anything the store cannot\n * vouch for is left as it is, for `validate-creatives` to report.\n */\nexport async function promoteCanvasOutputPaths(\n canvas: unknown,\n canvasDir: string,\n workspaceRoot: string,\n durableUrlFor: DurableUrlFor,\n log: (line: string) => void = () => undefined,\n): Promise<{ canvas: unknown; rewrites: CanvasPathRewrite[] }> {\n if (!canvas || typeof canvas !== \"object\") return { canvas, rewrites: [] };\n const nodes = (canvas as { nodes?: unknown }).nodes;\n if (!Array.isArray(nodes)) return { canvas, rewrites: [] };\n\n const outputsRoot = path.join(path.resolve(workspaceRoot), \"canvas\");\n const rewrites: CanvasPathRewrite[] = [];\n const promoted: unknown[] = [];\n // One at a time: each may read a large file and upload it, and the rewrites are reported in order.\n for (const node of nodes) {\n promoted.push(await promoteNode(node));\n }\n return rewrites.length === 0 ? { canvas, rewrites } : { canvas: { ...(canvas as object), nodes: promoted }, rewrites };\n\n async function promoteNode(node: unknown): Promise<unknown> {\n const n = node as { id?: unknown; type?: unknown; params?: Record<string, unknown> } | null;\n const params = n?.params;\n if (n?.type !== \"ingest\" || !params || params.source !== \"path\" || typeof params.path !== \"string\") return node;\n const from = params.path;\n const target = path.resolve(canvasDir, from);\n const inOutputs = path.relative(outputsRoot, target);\n if (inOutputs === \"\" || inOutputs.startsWith(\"..\") || path.isAbsolute(inOutputs)) return node;\n\n const nodeId = typeof n.id === \"string\" ? n.id : \"?\";\n const url = await durableCopyOf(target, inOutputs, durableUrlFor);\n if (url === null) {\n log(`[canvas] ${nodeId}: \"${from}\" is in canvas/, which is not committed, and no durable copy could be found for it`);\n return node;\n }\n const expect = typeof params.expect === \"string\" ? params.expect : \"\";\n if (!isAssetKind(expect) || canvasAssetMime(url, expect) === null) {\n log(`[canvas] ${nodeId}: \"${from}\" is in canvas/; its durable copy ${url} would not ingest back as ${expect || \"its kind\"}, so the path was left as it is`);\n return node;\n }\n const { path: _path, ...rest } = params;\n rewrites.push({ nodeId, param: \"source\", from, to: url });\n return { ...n, params: { ...rest, source: \"url\", url } };\n }\n}\n\nfunction isAssetKind(value: string): value is AssetKind {\n return [\"image\", \"video\", \"audio\", \"text\", \"json\", \"font\"].includes(value);\n}\n\n/** The durable URL for a file under `canvas/`, read from disk or, for a cache path, from its name. */\nasync function durableCopyOf(target: string, inOutputs: string, durableUrlFor: DurableUrlFor): Promise<string | null> {\n const bytes = await readFile(target).catch(() => null);\n if (bytes !== null) {\n return durableUrlFor({ sha256: sha256Hex(bytes), mime: inferMimeFromPath(target, bytes), bytes });\n }\n const address = /^\\.cache[/\\\\]assets[/\\\\]([0-9a-f]{2})[/\\\\]([0-9a-f]{64})\\.[a-z0-9]+$/.exec(inOutputs);\n if (!address?.[2] || !address[2].startsWith(address[1] ?? \"\")) return null;\n return durableUrlFor({ sha256: address[2], mime: inferMimeFromPath(target, Buffer.alloc(0)) });\n}\n\n/**\n * The store's own answer, through the backend: the public URL it would serve this sha\n * from, confirmed by a HEAD, and the bytes kept when they are in hand and missing. Asked\n * as `purpose: \"source\"` because the URL is about to be written into a committed file,\n * which pins the object against the asset sweep. Without a Baker connection nothing can\n * be vouched for, so nothing is rewritten and the commit gate reports the path instead.\n */\nexport function backendDurableUrlFor(log: (line: string) => void = () => undefined): DurableUrlFor {\n const connection = optionalConnection();\n if (!connection) return async () => null;\n const client = new BackendClient(connection);\n return async ({ sha256, mime, bytes }) => {\n try {\n const { putUrl, publicUrl, conditional } = await client.presignAssetUpload(sha256, mime, undefined, \"source\", true);\n const held = await fetch(publicUrl, { method: \"HEAD\" }).then(\n (res) => res.ok,\n () => false,\n );\n if (held) return publicUrl;\n if (!bytes) {\n log(`[canvas] the asset store does not hold ${sha256.slice(0, 12)}…, and the file is not here to keep`);\n return null;\n }\n const put = await fetch(putUrl, {\n method: \"PUT\",\n body: new Uint8Array(bytes),\n headers: conditional ? { \"Content-Type\": mime, \"If-None-Match\": \"*\" } : { \"Content-Type\": mime },\n });\n if (put.ok || (conditional === true && put.status === 412)) return publicUrl;\n log(`[canvas] could not keep ${sha256.slice(0, 12)}… in the asset store (upload answered ${put.status})`);\n return null;\n } catch (e) {\n log(`[canvas] could not reach the asset store for ${sha256.slice(0, 12)}…: ${e instanceof Error ? e.message : String(e)}`);\n return null;\n }\n };\n}\n","/**\n * A static ad scaffolded before the one-picture topology, upgraded in place on its next\n * render.\n *\n * The old graph rendered up to three takes (`ad_a`/`ad_b`/`ad_c`, or `ad`), judged them\n * (`pick`), then re-generated every other format from the winner (`ad_4x5`, `ad_9x16`) and\n * centre-cropped a shape the model lacked (`fit_4x5`). The upgrade keeps everything the\n * creative owns (prompt.json, the source ad, every element slot, sheet, logo and type\n * specimen, the generation model and its request params, the judge's model, the delivered\n * formats and their order) and swaps the render part for the graph `static-topology.ts`\n * builds: one request per variant, prefilter, judge, master, and sizes made from it.\n *\n * A canvas already on the current graph but scaffolded before the safe fit gets only that:\n * a `static_safe_fit` node after each 9:16 an interface covers, every other node kept, so\n * nothing upstream renders again.\n *\n * A one-picture graph planned with a retired master shape (the band master: a square master\n * whose scene was extended into the 9:16 and pasted back, the 4:5 cut from that) is rebuilt\n * the same way. Only a dev deployment that ran this branch's CLI can hold one.\n *\n * It runs in `prepareCanvas` before the brief projection, so the upgraded graph is what is\n * written, fingerprinted (a `v2:` fingerprint, which never reads as stale against the bare\n * one the old render recorded) and rendered. Run records of the old graph are untouched and\n * stay readable: every reader finds nodes by role and falls back to the old ids.\n *\n * Pure: canvas in, canvas out.\n */\nimport { pickImageModel } from \"../lib/image-model-router.ts\";\nimport type { NodeDecl } from \"../schema/canvas.ts\";\nimport {\n BRAND_LOGO_ROLE,\n isStaticBriefCanvas,\n STATIC_AD_ROLE,\n STATIC_CANDIDATE_ROLE,\n STATIC_RELAY_ROLE,\n} from \"./static-render-briefs.ts\";\nimport { DEFAULT_MASTER_SHAPE, MASTER_SHAPES, planStaticFormats } from \"./static-sizes.ts\";\nimport {\n formatSlug,\n LOGO_COMPOSITE_ROLE,\n STATIC_ADAPT_FORMAT_ROLE,\n STATIC_ADAPT_ROLE,\n STATIC_MASTER_ROLE,\n STATIC_PICK_ROLE,\n STATIC_SAFE_FIT_ROLE,\n STATIC_SIZE_ROLE,\n STATIC_VARIANT_CALL_ROLE,\n STATIC_VARIANTS_ROLE,\n safeFitNode,\n staticRenderNodes,\n VARIANT_PREFILTER_ROLE,\n} from \"./static-topology.ts\";\nimport { describeSizes, renderParamsFor } from \"./staticAd.ts\";\n\ntype Node = {\n id: string;\n type: string;\n params?: Record<string, unknown>;\n inputs?: Record<string, unknown>;\n tags?: { role?: string };\n};\n\nconst TAKE_ID = /^ad(?:_[a-c])?$/;\nconst RELAY_ID = /^ad_(\\d+)x(\\d+)$/;\nconst FIT_ID = /^fit_(\\d+)x(\\d+)$/;\nconst SIZE_ID = /^(?:size|safe|logo)_([0-9_]+x[0-9_]+)$/;\nconst RATIO = /^\\d+(?:\\.\\d+)?:\\d+$/;\nconst REF_ID = /\\$ref:([a-z][a-z0-9_]*)\\.[a-z_]+(?:#\\d+)?/g;\n\n/** A placeholder prompt: the brief projection compiles the real one right after. */\nconst UNCOMPILED = \"compiled from prompt.json\";\n\n/** Request params a take carried that are the model's own, not the shape, the count or the prompt. */\nfunction carriedParams(take: Node): Record<string, unknown> {\n const { prompt: _p, aspect_ratio: _a, n: _n, model: _m, ...rest } = take.params ?? {};\n return rest;\n}\n\nfunction refsOf(value: unknown): string[] {\n const list = Array.isArray(value) ? value : value === undefined ? [] : [value];\n return list.filter((r): r is string => typeof r === \"string\");\n}\n\n/** How a sentence of the old description said the run was made, to be replaced by the new one. */\nconst OLD_RUN_SENTENCE =\n /(?:One|Two|Three) finished ads? (?:is|are) rendered[^.]*?(?:\\.|; (?:the other (?:\\d+ )?formats? (?:is|are) the chosen ad re-laid by the model\\.))/;\n/** How a one-picture graph's description said its sizes were made (`describeSizes`), to be replaced by the new one. */\nconst SIZES_SENTENCE =\n /Every size is made from the chosen picture, rendered once at [\\s\\S]*?\\.(?=\\s|$)(?: No size is rendered again\\.)?/;\n\n/** The roles of the band-master graph's extended tallest size: the frame, the extend request, the paste-back. */\nconst EXTENDED_ROLES = [\"static_extend_frame\", \"static_extend\", \"static_pasteback\"];\n/** Every role of the render part of a topology graph: what the upgrade replaces. */\nconst RENDER_ROLES = new Set([\n STATIC_VARIANTS_ROLE,\n STATIC_VARIANT_CALL_ROLE,\n VARIANT_PREFILTER_ROLE,\n STATIC_PICK_ROLE,\n STATIC_MASTER_ROLE,\n STATIC_SIZE_ROLE,\n STATIC_ADAPT_ROLE,\n STATIC_ADAPT_FORMAT_ROLE,\n STATIC_SAFE_FIT_ROLE,\n LOGO_COMPOSITE_ROLE,\n ...EXTENDED_ROLES,\n]);\n\n/** What the old graph carried that the new one is built from. */\ntype OldGraph = {\n /** The render whose model, params and pictures the variants keep. */\n template: Node;\n takes: number;\n removed: Node[];\n judgeModel?: string;\n placements?: string[];\n compositeLogo?: string;\n /** The sentence of the old description that said how the run was made. */\n sentence: RegExp;\n};\n\n/**\n * The new graph for an old static canvas, and whether anything changed. Two older graphs\n * upgrade: the takes-and-re-lays graph (v1), and a one-picture graph whose master shape is\n * retired (the band master: the square extended into the 9:16 and pasted back). A canvas\n * already on the current graph, a hand-built one and anything that is not a static ad come\n * back as given.\n */\nexport function upgradeStaticCanvas(input: unknown): { canvas: unknown; upgraded: boolean } {\n const holder = input as { nodes?: unknown; metadata?: { description?: unknown } } | null;\n if (!holder || !Array.isArray(holder.nodes) || !isStaticBriefCanvas(holder))\n return { canvas: input, upgraded: false };\n const nodes = holder.nodes as Node[];\n const old = v1Graph(nodes) ?? retiredShapeGraph(nodes);\n if (!old) {\n const unjudged = withoutJudgeNode(holder, nodes);\n const fitted = withSafeFit(unjudged.canvas as { nodes?: unknown }, (unjudged.canvas as { nodes: Node[] }).nodes);\n return { canvas: fitted.canvas, upgraded: unjudged.upgraded || fitted.upgraded };\n }\n const removed = new Set(old.removed.map((n) => n.id));\n const final = nodes.find((n) => n.type === \"collect\" && n.id === \"final\");\n const labels = final?.params?.labels;\n const formats =\n Array.isArray(labels) && labels.length > 0 && labels.every((l) => typeof l === \"string\" && RATIO.test(l))\n ? (labels as string[])\n : [String(old.template.params?.aspect_ratio ?? \"9:16\")];\n const genModel = String(old.template.params?.model ?? \"\");\n const plan = planStaticFormats(genModel, formats, {\n masterShape: DEFAULT_MASTER_SHAPE,\n ...(old.placements ? { placements: old.placements } : {}),\n });\n const promptIngest = nodes.find((n) => n.type === \"ingest\" && (n.tags?.role === STATIC_AD_ROLE || n.id === \"prompt\"));\n const reference = refsOf(old.template.inputs?.reference).filter((r) => {\n const id = r.match(/^\\$ref:([a-z][a-z0-9_]*)\\./)?.[1];\n return id !== undefined && id !== \"original\" && !removed.has(id);\n });\n const built = staticRenderNodes({\n genModel,\n genParams: { ...renderParamsFor(genModel), ...carriedParams(old.template) },\n takes: old.takes,\n reference,\n specRef: `$ref:${promptIngest?.id ?? \"prompt\"}.asset`,\n judgeModel: old.judgeModel ?? pickImageModel(\"designed\", \"judge\"),\n adaptModel: pickImageModel(\"designed\", \"reframe\"),\n relayoutModel: pickImageModel(\"designed\", \"relayout\"),\n ...logoOf(nodes, reference),\n ...(old.compositeLogo ? { compositeLogo: old.compositeLogo } : {}),\n plan,\n prompt: UNCOMPILED,\n });\n\n // Anything hand-added that read an old render now reads what replaced it: the chosen\n // picture for a take, the judge or the master; the size for a re-lay, a crop or the\n // extended picture.\n const sizeRef = (format: string | undefined) => {\n const size = plan.sizes.find((s) => s.format === format);\n return size && size.from !== \"master\" ? `$ref:size_${formatSlug(size.format)}.image` : \"$ref:master.image\";\n };\n const extended = extendedFormat(nodes, formats);\n const replacement = (id: string): string => {\n const shaped = id.match(RELAY_ID) ?? id.match(FIT_ID);\n if (shaped) return sizeRef(`${shaped[1]}:${shaped[2]}`);\n const slug = id.match(SIZE_ID)?.[1];\n if (slug) return sizeRef(formats.find((f) => formatSlug(f) === slug));\n const node = nodes.find((n) => n.id === id);\n return node?.tags?.role && EXTENDED_ROLES.includes(node.tags.role) ? sizeRef(extended) : \"$ref:master.image\";\n };\n const rewire = (node: Node): Node => {\n if (!node.inputs) return node;\n const text = JSON.stringify(node.inputs);\n const next = text.replace(REF_ID, (ref, id: string) => (removed.has(id) && id !== \"final\" ? replacement(id) : ref));\n return next === text ? node : { ...node, inputs: JSON.parse(next) as Record<string, unknown> };\n };\n\n const firstRemoved = nodes.findIndex((n) => removed.has(n.id));\n const kept = nodes.filter((n) => !removed.has(n.id)).map(rewire);\n const at = nodes.slice(0, firstRemoved).filter((n) => !removed.has(n.id)).length;\n const description = holder.metadata?.description;\n const canvas = {\n ...holder,\n ...(typeof description === \"string\"\n ? { metadata: { ...holder.metadata, description: upgradedDescription(description, old, plan) } }\n : {}),\n nodes: [...kept.slice(0, at), ...(built as unknown as Node[]), ...kept.slice(at)] as unknown as NodeDecl[],\n };\n return { canvas, upgraded: true };\n}\n\n/**\n * A current graph scaffolded with its judge node (`pick`, reading the prefilter's variants):\n * the node goes, everything that read it reads the prefilter's chosen picture, and the prefilter\n * takes the judge's model, asking it only on a real tie (bench, Sept 2026: it decided 5 of 59\n * runs). Every other node is kept as it is.\n */\nfunction withoutJudgeNode(holder: { nodes?: unknown }, nodes: Node[]): { canvas: unknown; upgraded: boolean } {\n const prefilter = nodes.find((n) => n.tags?.role === VARIANT_PREFILTER_ROLE);\n const pick = nodes.find(\n (n) =>\n n.type === \"image_select\" &&\n n.tags?.role === STATIC_PICK_ROLE &&\n n.inputs?.images === `$ref:${prefilter?.id}.images`,\n );\n if (!prefilter || !pick) return { canvas: holder, upgraded: false };\n const chosen = `$ref:${prefilter.id}.image`;\n const takes = nodes.filter((n) => n.type === \"image_generate\" && n.tags?.role === STATIC_VARIANT_CALL_ROLE).length;\n const judge = typeof pick.params?.model === \"string\" && takes > 1 ? { judge_model: pick.params.model } : {};\n const next = nodes.flatMap((n): Node[] => {\n if (n.id === pick.id) return [];\n if (n.id === prefilter.id) return [{ ...n, params: { ...n.params, ...judge } }];\n if (!n.inputs) return [n];\n const text = JSON.stringify(n.inputs);\n const rewired = text.replace(new RegExp(`\\\\$ref:${pick.id}\\\\.images(?:#0)?`, \"g\"), chosen);\n return [rewired === text ? n : { ...n, inputs: JSON.parse(rewired) as Record<string, unknown> }];\n });\n return { canvas: { ...holder, nodes: next }, upgraded: true };\n}\n\n/**\n * A current graph without the safe fit, given one after each 9:16 Reels or Stories covers:\n * between the size (or the master, or what the logo composite reads) and its reader. Every\n * other node is kept as it is, so their cached outputs still hold.\n */\nfunction withSafeFit(holder: { nodes?: unknown }, nodes: Node[]): { canvas: unknown; upgraded: boolean } {\n const prefilter = nodes.find((n) => n.tags?.role === VARIANT_PREFILTER_ROLE);\n const final = nodes.find((n) => n.type === \"collect\" && n.id === \"final\");\n const labels = final?.params?.labels;\n const images = final?.inputs?.images;\n if (\n !prefilter ||\n nodes.some((n) => n.tags?.role === STATIC_SAFE_FIT_ROLE) ||\n !Array.isArray(labels) ||\n !Array.isArray(images)\n )\n return { canvas: holder, upgraded: false };\n const raw = prefilter.params?.placements;\n const placements = Array.isArray(raw) && raw.every((p) => typeof p === \"string\") ? (raw as string[]) : undefined;\n let next = [...nodes];\n const byId = (id: string | undefined) => next.find((n) => n.id === id);\n const idOf = (ref: unknown) => (typeof ref === \"string\" ? ref.match(/^\\$ref:([a-z][a-z0-9_]*)\\./)?.[1] : undefined);\n labels.forEach((format, i) => {\n if (typeof format !== \"string\") return;\n // What the final (or the logo composite before it) reads for this format.\n const composite = byId(idOf(images[i]));\n const reader = composite?.tags?.role === LOGO_COMPOSITE_ROLE ? composite : undefined;\n const source = reader ? reader.inputs?.image : images[i];\n const fit = typeof source === \"string\" ? safeFitNode(format, source, placements) : null;\n const from = next.findIndex((n) => n.id === idOf(source));\n if (!fit || from < 0 || byId(fit.id)) return;\n const fitRef = `$ref:${fit.id}.image`;\n next = next.map((n) => {\n if (reader && n.id === reader.id) return { ...n, inputs: { ...n.inputs, image: fitRef } };\n const read = n.inputs?.images;\n if (!reader && n.id === \"final\" && n.type === \"collect\" && Array.isArray(read))\n return { ...n, inputs: { ...n.inputs, images: read.map((r, j) => (j === i ? fitRef : r)) } };\n return n;\n });\n next.splice(from + 1, 0, fit as unknown as Node);\n });\n if (next.length === nodes.length) return { canvas: holder, upgraded: false };\n return { canvas: { ...holder, nodes: next }, upgraded: true };\n}\n\n/** The takes-and-re-lays graph (v1): up to three takes, a judge, re-lays and centred crops. */\nfunction v1Graph(nodes: Node[]): OldGraph | null {\n const takes = nodes.filter(\n (n) => n.type === \"image_generate\" && (n.tags?.role === STATIC_CANDIDATE_ROLE || TAKE_ID.test(n.id)),\n );\n const first = takes[0];\n if (!first) return null;\n const relays = nodes.filter(\n (n) => n.type === \"image_generate\" && (n.tags?.role === STATIC_RELAY_ROLE || RELAY_ID.test(n.id)),\n );\n const pick = nodes.find((n) => n.type === \"image_select\" && n.id === \"pick\");\n const fits = nodes.filter((n) => n.type === \"imagemagick\" && FIT_ID.test(n.id));\n const final = nodes.find((n) => n.type === \"collect\" && n.id === \"final\");\n return {\n template: first,\n takes: Math.min(3, takes.length),\n removed: [...takes, ...relays, ...(pick ? [pick] : []), ...fits, ...(final ? [final] : [])],\n ...(typeof pick?.params?.model === \"string\" ? { judgeModel: pick.params.model } : {}),\n sentence: OLD_RUN_SENTENCE,\n };\n}\n\n/**\n * A one-picture graph planned with a master shape that no longer exists (`band_master`), or\n * carrying its extended-size nodes: the whole render part is rebuilt on the current shape,\n * keeping the variants' model, params, pictures and count, the judge, the ad set's\n * placements and a composited logo.\n */\nfunction retiredShapeGraph(nodes: Node[]): OldGraph | null {\n const prefilter = nodes.find((n) => n.tags?.role === VARIANT_PREFILTER_ROLE);\n const shape = prefilter?.params?.master_shape;\n const retired =\n (typeof shape === \"string\" && !(MASTER_SHAPES as readonly string[]).includes(shape)) ||\n nodes.some((n) => n.tags?.role !== undefined && EXTENDED_ROLES.includes(n.tags.role));\n if (!retired) return null;\n const calls = nodes.filter((n) => n.type === \"image_generate\" && n.tags?.role === STATIC_VARIANT_CALL_ROLE);\n const template = calls[0] ?? nodes.find((n) => n.type === \"image_generate\" && n.tags?.role === STATIC_VARIANTS_ROLE);\n if (!template) return null;\n const pick = nodes.find((n) => n.tags?.role === STATIC_PICK_ROLE && n.type === \"image_select\");\n const placements = prefilter?.params?.placements;\n const composite = nodes.find((n) => n.tags?.role === LOGO_COMPOSITE_ROLE)?.inputs?.logo;\n return {\n template,\n takes: Math.min(3, Math.max(1, calls.length)),\n removed: nodes.filter(\n (n) => (n.tags?.role && RENDER_ROLES.has(n.tags.role)) || (n.id === \"final\" && n.type === \"collect\"),\n ),\n ...(typeof pick?.params?.model === \"string\" ? { judgeModel: pick.params.model } : {}),\n ...(Array.isArray(placements) && placements.every((p) => typeof p === \"string\") ? { placements } : {}),\n ...(typeof composite === \"string\" ? { compositeLogo: composite } : {}),\n sentence: SIZES_SENTENCE,\n };\n}\n\n/** The format the band master's extended picture was delivered as: the one the final read from the paste-back. */\nfunction extendedFormat(nodes: Node[], formats: readonly string[]): string | undefined {\n const pasteback = nodes.find((n) => n.tags?.role === \"static_pasteback\")?.id;\n const images = nodes.find((n) => n.type === \"collect\" && n.id === \"final\")?.inputs?.images;\n const at = Array.isArray(images) && pasteback ? images.indexOf(`$ref:${pasteback}.image`) : -1;\n return at >= 0 ? formats[at] : undefined;\n}\n\n/** The logo file among the references the model draws from, so each re-laid-out size is handed it too. */\nfunction logoOf(nodes: Node[], reference: string[]): { logoRef?: string } {\n const logo = nodes.find((n) => n.type === \"ingest\" && n.tags?.role === BRAND_LOGO_ROLE);\n const ref = logo ? reference.find((r) => r.startsWith(`$ref:${logo.id}.`)) : undefined;\n return ref ? { logoRef: ref } : {};\n}\n\nfunction upgradedDescription(text: string, old: OldGraph, plan: ReturnType<typeof planStaticFormats>): string {\n const now =\n old.sentence === SIZES_SENTENCE\n ? describeSizes(plan)\n : old.takes > 1\n ? `${[\"\", \"One\", \"Two\", \"Three\"][old.takes]} variants of the finished ad are rendered and a judge keeps one. ${describeSizes(plan)}`\n : `One finished ad is rendered (no judge). ${describeSizes(plan)}`;\n return old.sentence.test(text) ? text.replace(old.sentence, now) : `${text} ${now}`.trim();\n}\n"],"mappings":"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","names":["ref","plannedPlacements","path","ref","f","readdir","readFile","path","path","readFile","fromTokens","readdir","nodesOf","readFile","path","z","ref","nodesOf","readFile","path","ids","ref","z","z","isCast","z","z","NAMES","z","z","refId","isCast","refId","z","z","readFile","readdir","writeFile","path","readFile","writeFile","path","writeFile","path","readFile","readdir","readFile","path","readFile","path","ref","readdir","mkdir","readFile","writeFile","path","z","z","path","readFile","mkdir","writeFile","createHash","mkdir","readFile","writeFile","path","z","Answer","z","path","createHash","readFile","mkdir","writeFile","readFile","writeFile","readFile","writeFile","readdir","readFile","writeFile","path","createHash","mkdir","readFile","writeFile","path","createHash","mkdir","readFile","writeFile","path","z","z","Answer","unheld","_","path","createHash","readFile","mkdir","writeFile","path","createHash","readFile","mkdir","writeFile","path","path","readFile","path","path","readFile","UNCOMPILED","ref","path","readFile","readdir","quotes","writeFile"]}