Skip to content

Daily Drop & Character Roster

social/daily-drop is the multi-character fan-out workflow. Point it at a roster YAML, and it produces one or more talking-head videos per character in parallel — with a hard, pre-flight cost cap so misconfiguration can’t quietly empty your FAL credit pool.

Rosters are portable, declarative documents in YAML, TOML, or JSON — format selected by extension. The Fabric team’s example roster is checked in at examples/character-rosters/fabric-team.yaml; your own roster lives wherever you point roster_path at, with default auto-lookup at ~/.fabric/characters.{yaml,toml,json}.

roster_name: my-team
daily_budget_usd: 10.00 # hard preflight cap (USD/day)
shared:
language: en
duration_secs: 30
cost_path: cheap-fal # local | cheap-fal | premium-fal
characters:
- name: builder # alnum / -/_ only — used as artifact prefix
voice_style: "warm, technical"
gender: male
presenter_look: "engineer with glasses, soft window light"
persona: "indie engineer demystifying systems"
daily_video_count: 1
default_script_formula: youre_doing_it_wrong
allowed_formulas: [youre_doing_it_wrong, reframe, listicle]
topics_pool:
- "Why your AI workflow is leaking dollars"
- "Stop hand-prompting in chat — commit your prompts to Git"

The full schema is enforced via pydantic (workflows/social/_roster.py):

FieldRequiredDescription
roster_nameyesStable identifier — used as artifact filename prefix.
daily_budget_usdno (default 10)Hard cap. Preflight refuses to start if estimated spend exceeds this.
shared.cost_pathno (default local)local / cheap-fal / premium-fal — picks the model preset.
shared.duration_secsno (default 30)Per-drop target duration.
characters[*].nameyesSlug-safe character name.
characters[*].topics_poolyes (for unattended runs)Topic strings rotated by slot_offset.
characters[*].daily_video_countno (default 1, max 10)Drops per character per call.
characters[*].default_script_formulanoDefault narrative formula — see Script Formulas.
characters[*].allowed_formulasnoRotation pool overrides default_script_formula.
PathTextTTSAvatar/LipsyncPer-drop estimate
localgemini-2.5-flashKokoro (local)SadTalker (local)~$0.025
cheap-falgemini-2.5-flashFAL KokoroKling Avatar v2~$0.45
premium-falgemini-2.5-flashElevenLabs Turbo v2.5Kling Avatar v2~$0.55

Override individual models per-character (avatar_model, tts_model, text_model) or roster-wide (shared.avatar_model, etc.) when you need something off-preset. Resolution order: character > shared > preset.

The most ergonomic path is a self-contained run-spec file (plan 075):

Terminal window
fab-workflow --from-file examples/run-inputs/daily-drop-fabric-team.yaml

The file names both the workflow and the input — it’s a single artifact you can commit, hand to a teammate, or modify per-run. CLI --input k=v flags still layer on top:

Terminal window
fab-workflow --from-file examples/run-inputs/daily-drop-fabric-team.yaml \
--input slot_offset=$(date +%j)

See the examples/run-inputs/ directory for starter files.

Terminal window
fab-workflow social/daily-drop

Looks for ~/.fabric/characters.{yaml,yml,toml,json} only. Repo examples are intentionally not auto-discovered.

Terminal window
fab-workflow social/daily-drop --input roster_path=./my-roster.yaml
fab-workflow social/daily-drop --input roster_path=./my-roster.toml
fab-workflow social/daily-drop --input roster_path=./my-roster.json
Terminal window
fabric run social/daily-drop --input slot_offset=$(date +%j)

slot_offset rotates each character’s topics_pool and allowed_formulas so consecutive runs produce different topics. A common pattern: schedule the workflow once per day with slot_offset set to the day-of-year, so a 7-topic pool yields 7 unique days before repeating.

Before any heavy stage runs, daily-drop sums the estimated cost across every (character × slot × stage) against cost.PROVIDER_COSTS. If the total exceeds daily_budget_usd, it raises:

RuntimeError: daily-drop preflight: estimated $24.50 exceeds
daily_budget_usd=$10.00.
Per-stage breakdown:
builder:avatar: $9.0000
builder:tts: $1.5000
ops:avatar: $9.0000
...
Either raise daily_budget_usd, lower daily_video_count, or switch
shared.cost_path to 'local'.

This is a hard fail — the workflow refuses to begin. Cost overrun is the fastest way to ruin a dogfood run, so the preflight is intentionally strict.

{
"roster_name": "my-team",
"drops": [
{
"character": "builder",
"slot": 0,
"topic": "Why your AI workflow is leaking dollars",
"script_formula": "youre_doing_it_wrong",
"video_path": "/.../my-team-builder-slot0.mp4",
"asset_id": "asset_abc123",
"script_text": "You're paying for tokens you don't actually need...",
"error": ""
}
],
"total_estimated_usd": 1.35,
"success_count": 3,
"failure_count": 0
}

Failed drops carry error text instead of aborting the whole run — one flaky FAL call shouldn’t kill the rest of the day’s content. Check failure_count and the per-drop error fields after each run.

If you’re running the official Fabric character roster, the operating playbook lives at specs/playbooks/01-fabric-character-roster.md. It documents the daily / weekly / monthly review cadence, what success looks like, and the discipline around revenue claims (we don’t make them without 6 months of audited data).