Skip to content

Fan-out Protocol

The standard procedure for shipping a validated workflow from one test account to all target accounts. Run this in order — do not skip or reorder steps.

When to fan out

A workflow is ready to fan out when it has been:

  1. Validated on the test account — typically 3 of 4 acceptable generations per prompt
  2. Visually QA passed — no AI-tell issues (extra fingers, melted faces, garbled limbs) across the test account's outputs
  3. Approved by the user for the test account specifically

If any of these are missing, keep iterating on the test account before fanning out.

The 7-step sequence

Step 1: Duplicate the workflow tab

One tab per target account. If the test was on Account A and you want to ship to Accounts B, C, D, E, duplicate Account A's tab 4 times and rename each duplicate to the target account.

Step 2: Reassign node IDs so every node in the file is globally unique

After duplication, every cloned tab will have the same node IDs as the source. Walk each non-source tab and give every node a fresh ID that does not collide with any other ID in the file.

Also rewrite every edge's source / target in that tab to the new IDs so internal wiring stays intact.

Standard scheme: per-tab integer offset. For tab index N (0 = source, 1, 2, 3...), add N * 100000 to every node ID and link reference in that tab. Deterministic, preserves ordering for diffs, makes the source tab a contiguous lookup.

Why globally unique IDs matter

Sheet-sync scripts, variant patchers, and the Generation Runner all key off node.id. Duplicate IDs across tabs force every tool to disambiguate by (tab, id) pairs, which is fragile — a wrong tab lookup silently edits the wrong node. Globally unique IDs make node.id a real primary key.

Verify before saving: len({n.id for tab in tabs for n in tab.nodes}) == total_node_count.

Step 3: Rename all nodes in each tab

Match the target avatar. Prefix node titles with the account code (e.g., "V4 Scene pre — Symptom Hook""Account-B Scene pre — Symptom Hook"). This is mechanical but catches orphaned references to the old account name.

Step 4: Swap the avatar character reference sheet

Set each account's character reference R2 URL on the avatar Media node in that tab. Pull the URLs from the avatar reference registry.

Step 5: Walk the per-avatar checklist

Apply each CUSTOMIZED item to the new tab's prompts (wardrobe, hand details, dialogue persona swaps, etc.). Do NOT touch anything flagged STANDARD.

The checklist comes from the variant-creation phase (see Variants & Versioning). It has two columns:

STANDARD (do not change) CUSTOMIZED PER AVATAR (swap per account)
Base scene composition Avatar character reference sheet (R2 URL)
Camera angle, lighting direction Avatar name in node titles
Aesthetic / color palette Wardrobe / clothing description
Compliance rules Hand / watch / accessories detail
Product reference images Voice characteristics in Veo prompts
Script beats and dialogue structure Persona-specific dialogue references
Timing / word budgets Any account-specific setting or prop overrides

Step 6: Verify STANDARD items are byte-identical across tabs

After fan-out, run a quick diff of Prompt node text for anything in the STANDARD column. The diff should return empty. Any drift here is a bug.

A quick check:

import json
data = json.load(open(p))
tabs = data['tabs']
source_tab = tabs[0]
for n in source_tab['graphData']['nodes']:
    if n.get('type') == 'nanobanana/Prompt' and not is_customized(n):
        source_text = n['properties']['text']
        for tab in tabs[1:]:
            matching = find_corresponding_node(tab, n)
            assert matching['properties']['text'] == source_text, \
                f"STANDARD drift in {tab['name']} on node {matching['id']}"

Step 7: Run a single test generation per tab

Not a full workflow run — a single test generation (one node, one row) per tab to sanity-check the avatar swap before committing to full production runs.

If the test gen looks right (correct face, correct wardrobe, no obvious drift), proceed to the full Generation Runner pass for that tab.

The "if I want to change a STANDARD item" rule

If during fan-out you find yourself wanting to change a STANDARD item because it "reads better" for one account, stop. Either:

  • (a) Promote that change to STANDARD and apply it to all tabs uniformly, or
  • (b) Move it to the CUSTOMIZED list and document why this account needs the override.

Never silently diverge a single tab from the standard. Silent divergence makes the workflow impossible to maintain — future variant patches assume STANDARD items are shared, and silent drift breaks that assumption.

After fan-out

The fan-out itself bumps the version (it's a "meaningful change" under the versioning rules).

Specifically:

  • During testing: V0-N → V0-N+1 (fan-out is still part of the testing phase)
  • After approval: when the user explicitly approves the fan-out AND all accounts ship clean → graduate to V1

Record the fan-out in the version registry with a history note like "fan-out to Accounts B, C, D, E" — be specific, not vague.

File layout reminder

A multi-account workflow lives in one .nbflow file with multiple tabs, NOT split into per-account files.

  • File: {workflow}-{version}.nbflow (e.g. XYZG1-V0-3.nbflow)
  • Tabs: one per account, naming = account code (e.g. "Account A", "Account B")
  • Order: test account first, fan-out accounts after

If you find yourself splitting into per-account files, you're working against the system — the single-file pattern is the source of truth and what the Generation Runner, sheet sync, and variant patcher all assume.