The first stage of the reel factory is a function that takes a topic and returns a script. The downstream stages (image generation, voice, ffmpeg) need structured data, so the script has to be real JSON every time, not prose that looks like JSON.
There was no finished reel to write up this week, so this post describes the code in src/script.js instead of a specific video.
The problem
A model asked for a script will happily wrap it in a markdown fence, add a friendly sentence before it, or return three scenes when you needed four. Any of those breaks the pipeline halfway through, after you have already paid for images.
The steps
- Keep the brand rules in files, not in code. The factory reads
style.md(voice, banned topics, scene structure),pillars.mdandproducts.yml(the only links a call to action may point at) and pastes them into the system prompt. Changing the tone is an edit to a text file. - Put the schema in the user prompt. The prompt lists the exact keys:
hook,title,scenes(each withon_screen_text,voiceoverandimage_prompt),caption,hashtags,cta,ai_disclosureandpillar. It says to return only a JSON object, with no fences and no commentary, and spells out the limits: four scenes, a maximum of nine words for the hook, and a total voiceover length. - Parse defensively. The parser trims the text, strips a leading code fence if there is one, then slices from the first
{to the last}before callingJSON.parse. That handles the common case of a polite preamble. - Validate the parts you depend on. After parsing, the code checks that
scenesis an array with at least three entries and throws otherwise. It then trims to five scenes at most, lowercases hashtags and removes any leading#, and caps them at ten. - Force the fields that must never vary.
ai_disclosureis set totruein code regardless of what the model said, andpillarfalls back to the topic’s pillar if it is missing. Rules that matter should not depend on the model remembering them. - Retry, and cool down. The function makes up to three attempts. The first uses temperature 0.7, and the retries use 0.4, so a failed parse gets a more conservative second try. If all three fail, it throws one clear error that includes the last parse message.
What breaks
The model ignores a count. Asking for exactly four scenes does not guarantee four. The factory only enforces a floor of three and a ceiling of five, so a script with a different count still goes through. If you need an exact count, check it and retry.
Trailing prose with braces. Slicing between the first and last curly brace is a heuristic. If the model adds a closing remark that contains a brace, the slice will include it and parsing fails. The retry usually fixes it, but it is a reason to keep the instructions firm about “only a JSON object”.
Silent rule drift. The prompt says the voiceover should be a certain length and that every claim must be verifiable, but the code does not measure either. Those checks are on me when I approve each reel on Telegram. Anything you can check in code, check in code.
My take
Add a small schema validator (for example, a few lines that check every scene has all three string fields) before you spend money on images. It is cheap, and it moves failures to the start of the pipeline where they cost nothing.
Try it
The free workflows and the tools I use are at wrenbench.com.
Wrenbench