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.
Roster schema
Section titled “Roster schema”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-teamdaily_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):
| Field | Required | Description |
|---|---|---|
roster_name | yes | Stable identifier — used as artifact filename prefix. |
daily_budget_usd | no (default 10) | Hard cap. Preflight refuses to start if estimated spend exceeds this. |
shared.cost_path | no (default local) | local / cheap-fal / premium-fal — picks the model preset. |
shared.duration_secs | no (default 30) | Per-drop target duration. |
characters[*].name | yes | Slug-safe character name. |
characters[*].topics_pool | yes (for unattended runs) | Topic strings rotated by slot_offset. |
characters[*].daily_video_count | no (default 1, max 10) | Drops per character per call. |
characters[*].default_script_formula | no | Default narrative formula — see Script Formulas. |
characters[*].allowed_formulas | no | Rotation pool overrides default_script_formula. |
Cost paths
Section titled “Cost paths”| Path | Text | TTS | Avatar/Lipsync | Per-drop estimate |
|---|---|---|---|---|
local | gemini-2.5-flash | Kokoro (local) | SadTalker (local) | ~$0.025 |
cheap-fal | gemini-2.5-flash | FAL Kokoro | Kling Avatar v2 | ~$0.45 |
premium-fal | gemini-2.5-flash | ElevenLabs Turbo v2.5 | Kling 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.
Running it
Section titled “Running it”Recommended: sharable run-spec file
Section titled “Recommended: sharable run-spec file”The most ergonomic path is a self-contained run-spec file (plan 075):
fab-workflow --from-file examples/run-inputs/daily-drop-fabric-team.yamlThe 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:
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.
Default-lookup roster
Section titled “Default-lookup roster”fab-workflow social/daily-dropLooks for ~/.fabric/characters.{yaml,yml,toml,json} only. Repo
examples are intentionally not auto-discovered.
Explicit roster path
Section titled “Explicit roster path”fab-workflow social/daily-drop --input roster_path=./my-roster.yamlfab-workflow social/daily-drop --input roster_path=./my-roster.tomlfab-workflow social/daily-drop --input roster_path=./my-roster.jsonSlot rotation across the day
Section titled “Slot rotation across the day”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.
Pre-flight cost cap
Section titled “Pre-flight cost cap”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 exceedsdaily_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 switchshared.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.
Output shape
Section titled “Output shape”{ "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.
Dogfood playbook
Section titled “Dogfood playbook”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).