workflow-combo-loop — procedure detail
Commands
python3 scripts/combo_loop/refresh.py --brand dawn # READ + RECODE + UPDATE (Meta GET, Sonnet on new concepts, cap 40)
python3 scripts/combo_loop/refresh.py --brand dawn --no-code # read + aggregate only
python3 scripts/combo_loop/refresh.py --brand dawn --offline # re-aggregate today's saved ads.json/weekly.json (no network)
python3 scripts/combo_loop/plan.py --format format-song --brand dawn --n 4
python3 scripts/combo_loop/tags.py keys
python3 scripts/combo_loop/tags.py check output/concept-packs/<slug>.md
python3 scripts/combo_loop/tags.py register --prefix N1006-A combo_avatar=parent-teen combo_angle=reframe ...
Files
| Path | What |
|---|---|
scripts/combo_loop/brands.json |
per brand: ad account, breakeven ROAS, purchase action, codebook, stake bank, baseline dir, pack globs, hub sessions |
scripts/combo_loop/codebook_dawn.md |
14 fields; the table is parsed for allowed values (tags.py, coding validation) |
output/combo-loop/dawn/codes.json |
cumulative codes; each has match.concept_keys (body hash) or match.ad_bases (ad base names) and source |
output/combo-loop/dawn/<date>/ |
snapshot: MAP.md, recipes.json, units.json, agg.json, family.json, ads.json, weekly.json |
output/combo-loop/dawn/2026-10-05/ |
baseline rebuilt from the 10-05 codes (spend/ROAS as coded that day) so the first diff is apples to apples |
Ad ↔ code assignment (per ad, first match wins)
- Tag prefix: normalized ad name (strip
LP Split ·,LW ·,· …,- Copy…,- SCALE…) starts with a taggedcombo_ad_prefix(longest prefix wins). Tag sources: pack front matter,job.json/spec.json(combo_*keys or a"combo"block), hubads.jsonentries (combo_*+meta_ad_name),output/combo-loop/<brand>/tagged/*.md. - Ad base name in a code's
match.ad_bases(the 10-05 songs were coded by base name). - Concept key (ads ≥$100):
S:/V:+ md5 of the first 400 chars of the primary text (+ post id for video), as 10-05group.py. - Else: ads ≥$100 form NEW concepts (grouped by key); concepts ≥$100 go to Sonnet, biggest spend first, cap 40.
Launch scripts name ads ad hoc (sl0926-<slug>, 9-30 it · <id>, N1002-A1…): the tag's prefix must be set to what the launch script will write. Hub sessions already carry meta_ad_name on some ads (25 on 10-06); those scripts are fed to the coder.
Classification + recipes
- Breakeven ROAS
beper brand (Dawn 1.32). W: ≥$300 & ROAS ≥ be×1.10; L: ≥$300 & ROAS < be×0.91; W?: $150-300 & ≥ be×1.14; L?: $100-300 & < be×0.76 (same cut-offs as 10-05). - Families from the coded
format: song (song-longform-animation, song-short), video (cartoon-vo, ugc-talking), static (image-*, listicle-link). - Recipe per dimension: among values with spend ≥ max($300, 3% of family spend), the highest spend-weighted ROAS. Confidence: high = n≥5, ≥$10K, ≥3 W; med = n≥3, ≥$2K, ≥1 W; low = otherwise; "unprofitable" if the best value is under breakeven. Avoid list = values under be×0.91.
- Pairs: every two-dimension combination per family (static: n≥3 & ≥$3K; song/video: n≥2 & ≥$300).
- Frequency/decay per family: CPM, $-weighted unit frequency (impressions ÷ summed ad reach), impressions per $, link CTR, CPA; per unit ≥$300: weeks live, weeks ≥$100, peak week, CPA weeks 1-2 vs weeks 3+ (decay ratio = late CPA ÷ early CPA; needs ≥$100 late spend).
Plan logic
- Parents = the family's W/W? units ranked by spend × ROAS (top 2).
- Proposals: exploit (parent's value ≠ recipe winner with confidence ≥ low and ≥0.05 ROAS lift), stake swap (bank rows, skipping avoid categories and kid-future money; mom-landing first; ≤2), explore (a med/high winner from another family never tried here). One per dimension, ranked by confidence × lift.
- Thin family (n<5 or no paying parent) → proxy family: story formats → song, callouts → static; the block says so.
ILLUSTRATIVE run (shape only; numbers come from the live refresh)
$ python3 scripts/combo_loop/plan.py --format format-courtroom --brand dawn --n 3
## Combo plan — format-courtroom · dawn · <date>
> Family `video` has N coded units and 0 paying parents: planning from the `song` recipe as the story proxy …
### C1 [exploit] stake_category: FAMILY/RELATIONSHIP → LEGAL
- Parent: <paying song> ($…, ROAS …, CPA $…)
```yaml
combo_avatar: parent-teen
...
combo_variable: stake
combo_ad_prefix: ""
``
Then: paste intooutput/concept-packs/front matter, setcombo_ad_prefix,tags.py check`, continue with the format skill.