Brevo Ecommerce · Performance Creative

Ad Intelligence Agent

A weekly competitor sweep that turns Meta and Google ad libraries into an angle inventory, then drafts our own ads against it. Architecture, agent logic, dashboard spec and build plan.

Phase 0 output: the angle taxonomy → Built from 268 competitor ads. Gate passed.
Phase 1: the live intelligence layer → Deployed on Cloudflare. Sweeps weekly, 215 ads held.

22 September 2026 · v2, decisions settled · written against verified API access, not assumptions

Contents
  1. The thesis
  2. What already works
  3. Four hard truths
  4. Persistence Score
  5. The angle inventory
  6. Positioning Constitution
  7. The six agent loops
  8. Data model
  9. Static and video generation
  10. Draft delivery
  11. Dashboard spec
  12. Metrics catalogue
  13. Alerts
  14. The chat console
  15. Stack and build plan
  16. Risks and open questions

01The thesis

The instinct is to build an ad generator with a competitor feed bolted on. That is the wrong centre of gravity, because generation is the part we already solved. There is a working static pipeline that has shipped 247 ads through a ranked template library with brand safety, chroma compositing and a vision QA gate. Adding another image model call is not the hard part.

The hard part is judgment. Ad libraries are a firehose of creative with no performance data attached. Meta publishes no spend, no impressions and no CTR for commercial ads. Google publishes only where and how long. So an agent that "looks at competitor ads and makes similar ones" is copying noise: most of what Klaviyo is running is losing, and we cannot see which half.

The agent's real job is to infer which competitor ads are winning from public signals, decompose those winners into arguments rather than pictures, and then run our own arguments against our own performance data so the loop compounds.

Three consequences follow, and they should drive every build decision:

  1. The unit of intelligence is the angle, not the image. "Klaviyo added seven ads on the deliverability angle this week and retired the AI agents angle" is an insight. "Here are forty new JPEGs" is a folder.
  2. The unit of value is the join. Every ad we generate carries a pointer to the competitor angle it descended from. Once that ad runs, its real CTR and CPA flow back to the angle. After a quarter we can say which competitor arguments actually work for our audience. Nobody can buy that. It is the only reason to build instead of paying Atria.
  3. Copying is a validated hypothesis, not a shortcut. Klaviyo sells to merchants who already have a list and a budget. Brevo ecom has a top-of-funnel problem. An angle that converts for them can flop for us, so every adapted angle goes in as a test with a kill rule, not as a truth.

02What already works (checked today, not assumed)

Before designing anything I probed the access layer. This changes the build materially: most of the plumbing is already paid for and live.

CapabilityRouteState
Read our Meta ad account: campaigns, ad sets, ads, creatives, insightstreg connected account, meta-ads.*Live, $0 per call Returns real campaign history going back months.
Write to Meta: campaign, ad set, creative, ad, image and video uploadmeta-ads.x.campaign-create, .adset-create, .adcreative-create, .ad-create, .account-adimages, .account-advideosAvailable, $0 per call No Meta app, no App Review, because the account is connected by OAuth on our side.
Competitor Meta ads by pagescrapecreators.x.v1-facebook-adlibrary-company-ads resold through tregLive at $0.00188 per call on treg's key, which routes around our own expired ScrapeCreators credit.
Competitor Google ads by domain or advertiserscrapecreators.x.v1-google-company-ads, plus serpapi.google.ads.transparency at $0.015Live ScrapeCreators is still the only source that returns the YouTube URL behind a Google video ad.
Fallback scrapers if a route breaksApify facebook-ads-scraper, scrapesage/google-ads-transparency-scraper, and agent-browser for Cloudflare wallsAll previously verified
Image generationMagicFit API on GPT Image 2, plus Replicate and HiggsfieldLive Measured cost as low as 2 credits at 1K low, which is production quality for feed statics.
Video generationSeedance, Veo, Wan through treg or HiggsfieldUsable for b-roll and concept films only
Our own Google Ads accountgoogle-ads.query, google-ads.accounts exist in the catalogue but with zero samples on our teamNot connected This is the one real blocker. See risks.
Our own ScrapeCreators keydirect MCPOut of credits Irrelevant if we route via treg, which is cheaper anyway.

One more thing the probe surfaced: the Meta account already contains a campaign literally named DRAFT TEST | ... | 2026-09-03 sitting paused. The draft convention we need is already half in place by accident.

Correction on the Meta accounts

The connected account is PushOwl's, not Brevo's, despite being named "Brevo PushOwl". Brevo runs a second Meta account that is not connected yet. The two are used interchangeably to run ecommerce ads, so this is not a structural problem. Drafts land in the PushOwl account because it is already connected and writeable. Both accounts get tracked for the Compare and Performance screens once the Brevo one is connected.

Decisions settled

QuestionCall
TenancySingle tenant. Brevo and PushOwl are one business; PushOwl is the Shopify-app surface of Brevo, non-Shopify merchants reach the same product via plugins or directly. No multi-tenant data model, which removes a whole class of day-one complexity.
Where drafts landPushOwl Meta ad account, via the treg connection, paused top to bottom.
Data sourcing policyFree routes first. Open scrapers, public endpoints and GitHub skills are the default. Paid endpoints are only justified where a free route cannot return the exact fields at the same quality. Persistence Score depends entirely on delivery start date and active status, so any free route that drops those fields does not qualify and the paid route wins on that field alone.
ModelsLuna for decompose, the vision-to-schema pass that runs over thousands of creatives and is the highest-volume cost line. Claude Sonnet for all copywriting and brief generation. Sol only for the chat console, the one genuinely multi-step surface. Sweep, score and cluster use no model at all: they are SQL, diffs and embeddings. Models pinned by exact id, never by alias.
Existing static pipelineReference only. The template library, the fabrication gate and the edge QA logic are read for context and their lessons carried across, but the ad creation pipeline is rebuilt around the teardown method in section 09. The batch-script runner is not lifted.
Google AdsOAuth to be supplied. Until then Google is competitor intelligence only.

Competitor set

Ten brands, three tiers. Tier 1 and 2 are swept weekly, tier 3 monthly and read for craft rather than positioning.

TierBrandsWhy
1 · Direct, ecommerce nativeKlaviyo, Omnisend, Attentive, YotpoSame buyer, same category. Their angle inventory is the one we are fighting over.
2 · Horizontal and adjacentMailchimp, ActiveCampaign, AWeber, Sendlane, DripOverlapping buyer, different centre of gravity. Mailchimp and ActiveCampaign in particular run large, well-tested creative budgets whose formats travel.
3 · Craft referenceTriple WhaleNot a product competitor. Included because the creative craft in this segment is consistently better than the direct competitors, and formats are portable even when arguments are not.

03Four hard truths that shape the design

1. Ad libraries carry no performance data, so longevity is the only honest proxy

Meta gives an ad's start date and whether it is still active. Google gives firstShown, lastShown and shownForDays. That is the whole signal set. An advertiser with a competent media buyer kills losers inside 7 to 14 days, so an ad that has been live for 60 days is almost certainly paying for itself. Everything else we can infer, we infer from behaviour: how many variants they cut of one concept, whether they re-launched something they killed, how many regions they expanded it to. Build the scoring on those, and label it honestly in the UI as an inference, not a metric.

2. The library undercounts and mislabels, systematically

Google's own number_of_ads_estimate is rounded and overstates. Regions listed are where impressions were recorded, not where they targeted, and anything under roughly a thousand impressions is suppressed entirely. Meta keyword search returns other advertisers' ads, so results must always be filtered by page id. One brand routinely has several legal entities with separate advertiser ids, so a domain sweep that finds one entity is an incomplete sweep. Each of these is a silent data-quality bug that will quietly corrupt week-over-week deltas, so each needs an explicit guard in the ingest layer, not a comment in a README.

3. Meta has no real draft object, Google does

There is no "draft ad" in the Marketing API. The pattern that works is a real campaign created with status: PAUSED all the way down the tree, in a reserved campaign whose name marks it as agent output, so a human can open Ads Manager, preview the rendered ad, and flip it live. Google Ads is different: it has genuine CampaignDraft objects that stage changes against a base campaign, and it now does real-time policy review on responsive search ads, which means we can get a policy verdict before a human ever looks. Design the delivery layer per platform rather than pretending they are symmetric.

4. Naming a competitor in an ad is a legal decision, not a creative one

Our sharpest wedge is that Klaviyo charges for profiles you are not emailing. That is also the claim most likely to attract a letter. The agent must be able to generate the angle without naming the competitor by default, tag any variant that does name one, and route those to a human gate that is separate from the normal approve button. This is a switch in the Positioning Constitution, not a prompt suggestion.

04Persistence Score: the winner detector

One number per competitor ad, recomputed every sweep, from signals we can actually observe. Weights are a starting point to be tuned once we have our own outcome data to calibrate against.

SignalWhat it meansWeight
Days live, log scaledSurvived the kill decision. The strongest single signal.0.35
Still active nowCurrently surviving, not historically survived.0.15
Sibling countNumber of near-duplicate creatives in the same concept cluster. Buyers only iterate on winners, so a concept with 9 variants is a concept that works.0.20
Placement and surface breadthFeed plus Reels plus Stories, or Search plus Display plus YouTube. Scaling out costs money, so it signals confidence.0.10
Geo breadth, with the suppression caveatRegions where impressions were recorded. Treated as directional only.0.08
ResurrectionWent dark, then came back. A strong, rare and underused signal.0.07
Spend proxyAd count on this concept as a share of that advertiser's total live ads.0.05

Two derived views matter more than the raw score:

05The angle inventory

Every ingested ad, static or video, goes through one vision and text pass that returns a structured object. This is the schema that everything downstream depends on, so it is worth over-thinking once and then freezing.

{
  "angle":        "pricing-fairness",        // the argument, clustered
  "promise":      "stop paying for contacts you never email",
  "mechanism":    "billing on sends, not stored profiles",
  "audience_cue": "scaling DTC brand with a bloated list",
  "pain":         "bill grew faster than revenue",
  "objection":    "migration is painful",
  "proof_type":   "price-comparison | screenshot | stat | testimonial | logo-wall | demo | none",
  "offer":        "free tier to 100k contacts",
  "cta":          "compare your bill",
  "format":       "static | video | carousel | text | display",
  "visual_world": "invoice / spreadsheet / receipt",
  "hook_text":    "Your list is not your bill.",
  "hook_seconds": 2.4,                        // video only
  "emotion":      "indignation",
  "names_competitor": true,
  "seasonal_tag": "bfcm"
}

Clustering into angles should be embedding based over the promise plus mechanism fields, with a human-curatable label per cluster. Do not let the model invent a fresh angle name per ad or the inventory becomes unusable within two sweeps.

The outputs that make this worth doing:

06The Positioning Constitution

The single most important file in the repo, and the one that stops this becoming a machine for producing off-brand slop at scale. Every generated ad is validated against it before it can reach a draft. It is a checked-in document with a machine-readable validator, not a paragraph in a system prompt.

Hard constraints

Positive direction

The validator runs twice: on the copy before rendering, and as a vision pass on the rendered image, checking for unauthorised text, invented claims and misspellings. That second pass already exists and already caught a hallucinated money-back guarantee, so it should be lifted straight across.

07The six agent loops

Do not build one agent. Build six narrow ones behind a queue, each with a single responsibility and a checkable output. The chat console is a seventh that orchestrates them on demand.

Loop 1 · Sweep (weekly cron, plus on demand)

For each tracked brand, pull Meta page ads and Google advertiser ads. Diff against what we already hold. Write only new and changed rows. Update last_seen and mark disappearances. Enumerate all legal entities per domain, not just the first. On-demand mode accepts any brand name, domain or page id we have never tracked, resolves it, and runs the same path.

Loop 2 · Decompose

Vision and transcript pass over new creatives only. Statics go to a vision model, Meta videos through the ad transcript endpoint, Google video ads through the YouTube URL then transcript. Emits the angle object above. Costs money, so it runs once per creative, ever.

Loop 3 · Score and cluster

Recompute Persistence Score across the whole corpus, recluster angles, compute weekly deltas, detect entered and exited angles, write the digest.

Loop 4 · Adapt

Take the top scoring angles, filter for ICP fit against our funnel problem, run them through the Constitution, and emit briefs: the argument restated in our voice, with the hook variants, the proof we are allowed to use, and the format recommendation. This is a text-only loop and it is the one whose output a human should review most closely, because everything downstream inherits its mistakes.

Loop 5 · Produce

Statics rendered end to end from the existing template library and bake pipeline, with the fabrication gate and edge QA. Videos produce a shootable brief, not a file. Output lands in the gallery as a candidate, never as a draft.

Loop 6 · Measure

Pull our own Meta insights daily, join live ads back to the angle they descended from, compute per-angle and per-hook performance, detect fatigue, and feed the results back into the Adapt loop's ranking. This is the loop that makes the system compound, and it is the one most likely to get deferred. Do not defer it.

08Data model

Five tables. Keep metadata in D1 and every binary in R2, because a creative-heavy corpus will hit D1 limits fast and we have already had one read-cap outage on a different project.

TableHoldsNotes
brandsTracked competitors and ad-hoc lookups. Page ids, advertiser ids per legal entity, domains, tier.Ad-hoc brands flagged so they do not enter the weekly cron unless promoted.
adsEvery creative ever seen. Immutable body, mutable first_seen, last_seen, is_active, days_live, score.Never delete. Disappearance is data.
anglesCluster label, canonical promise, member count, half life, weekly velocity, whitespace flag.Human-editable labels.
creativesOur output. Links to source angle and source ad. Status: candidate, approved, rejected, drafted, live, killed. Revision history from the feedback field.The join table that carries all the value.
performanceDaily snapshots of our own ad metrics keyed on platform ad id.Snapshot daily rather than query on read, so trend and decay are cheap.

09Static and video generation

Static: the reverse-engineering method

This is the core of the production layer, and it is deliberately not "show the model a competitor ad and ask for something similar". That produces either plagiarism or mush. Six stages, each with a checkable output.

1 · Teardown into two layers, never prose

One vision pass per competitor static returns two objects. The argument layer is the angle schema from section 05. The craft layer is what actually stops the scroll, and it must be captured as structure:

{
  "device":        "annotated-invoice",     // the visual trick carrying the ad
  "zones": [
    {"role":"hook",    "x":0.06,"y":0.05,"w":0.88,"h":0.18,"z":1},
    {"role":"artifact","x":0.10,"y":0.26,"w":0.80,"h":0.48,"z":2},
    {"role":"callout", "x":0.55,"y":0.44,"w":0.34,"h":0.10,"z":3},
    {"role":"cta",     "x":0.06,"y":0.86,"w":0.50,"h":0.08,"z":4}
  ],
  "hierarchy":     ["hook","callout","artifact","cta","logo"],
  "type_system":   {"sizes":3,"weight_contrast":"900/400","case":"sentence",
                    "hook_height_pct":0.14},
  "palette":       ["#0B1F3A","#F4F1EA","#E23D2C"],  // sampled from the pixels
  "hook_contrast": 11.2,                              // ratio against its backdrop
  "negative_space":0.31,
  "proof_artifact":"invoice line item with a circled overage charge",
  "focal_points":  1
}

The lesson from the previous pipeline is load-bearing here: one-line descriptions rendered ads that read as obviously generated, and only a rich zoned spec carrying positions, type system and copy architecture produced work worth shipping. So the teardown is structural from the start.

2 · Abstract to a format

Strip the brand, the product and the claim. What survives is portable: annotated invoice, one circled line item, single red callout, hook above, sentence case, one focal point. That is a format. Formats are deduped across all ten competitors into a shared bank, each carrying the Persistence Score distribution of every ad built on it. A format used by four competitors with a median 70 days live is a validated container, independent of what anyone put inside it.

3 · Re-ground in Brevo, in a fixed order

Order matters because each step constrains the next.

  1. Argument is replaced with one of our angles from the Constitution. Never the competitor's claim rewritten.
  2. Proof artifact is replaced with a real Brevo artifact: an actual pricing table, a real UI capture, a number from the claims ledger. This is the single step where fabrication would enter the system, so it is gated: if no real artifact exists for that slot, the slot is dropped or the format is substituted for one that does not need it. The prior pipeline invented a money-back guarantee twice for a store with no returns policy, which is exactly the failure this gate exists to stop.
  3. Brand system comes from the brand kit: palette, type, logo lockup, safe area. The logo is composited from the real asset through a chroma slot rather than drawn by the model, because models reliably corrupt wordmarks.

4 · Uplift, deliberately

A re-skin is not the goal. Every adapted ad runs a craft uplift pass with explicit targets rather than vague instruction: hook type raised to the thumbnail-legible floor, exactly one focal point, hook contrast above threshold, fewer distinct type sizes than the source, more negative space than the source, and a rendered world in place of anything that reads as stock. The result is then scored against the source on the same craft rubric, and only variants that beat the source are kept. This is what turns "copy wisely" into something measurable instead of aspirational.

5 · Gates

Copy validator before render, against the Constitution and the claims ledger. Vision QA after render for unauthorised text, invented claims, logo fidelity, misspellings and thumbnail legibility. Safe-area and edge check. One hardened retry, then the candidate is dropped rather than shipped degraded.

6 · Ladder, not tweaks

Each approved brief produces the same argument across several different formats, not several nudges of one. Six to ten finished candidates per brief, so the review step is a real selection rather than take-it-or-leave-it. Sizes: Meta feed 1:1 and 4:5, Stories and Reels 9:16, Google Display in the standard set.

Google text and search

Fully generatable and cheap: 15 headlines and 4 descriptions per responsive search ad, built from the angle's promise and objection fields, plus sitelinks and callouts. Worth mining competitor text ads hard, because that copy is the most honest statement of a competitor's positioning they ever publish, and almost nobody analyses it.

Video, brief only

The output object is a shootable brief, and it should be graded on producibility so the pipeline is honest about what it is handing over:

The brief carries: the hook as on-screen text and as spoken line, a beat sheet with timecodes, shot list, b-roll list, the asset request addressed to a human, the source competitor video it was derived from with its timestamped hook, and the aspect ratios to cut. The highest-leverage input here is the competitor hook library built from transcripts: the first three seconds of every high-persistence competitor video, transcribed and clustered. That library is a standalone asset, useful even if nothing else ships.

10Draft delivery

Nothing reaches a platform without an explicit human click. Approve in the gallery is a review action. Push to platform is a separate, deliberate second action. The agent never pushes on a cron, and never unpauses anything. This is a hard gate in code, not a convention.

Meta

Upload image to the ad account image library, create the ad creative, create the ad against a reserved paused campaign and ad set named AGENT DRAFT | angle | date, with status: PAUSED at every level. The human opens Ads Manager, sees the real rendered preview across placements, and either flips it live or deletes it. Record the returned platform ad id on our creative row immediately, because that id is the key the Measure loop joins on later.

Google

Use real CampaignDraft objects for structural changes, and paused ad groups with paused responsive search ads for creative. Upload display images as assets. Take the real-time policy verdict on responsive search ads and surface it in the gallery before a human spends attention on a creative that will be disapproved anyway.

11Dashboard spec

Home · Console

The chat console at the top, occupying the primary position. Below it a compact week strip: new competitor ads, new angles, angles that died, our creatives awaiting review, our live ad count and the one metric that moved most. Below that, this week's digest in prose, written by the agent, three paragraphs, no more.

Radar · competitor ads

The gallery of everything ingested. Filters on brand, platform, format, angle, persistence band, active or dead, first seen, geo, names-a-competitor, seasonal tag. Sorts on score, days live, newest, sibling count. Each card shows the creative, days live, score, angle label, and a one-line "why this scored" explanation, because an unexplained score gets ignored. Card actions: adapt this, add to a brief, pin to a board, mute this advertiser.

Angles

The inventory. Share of voice stacked by competitor over time, half life table, velocity leaderboard, entered and exited this week, and the whitespace panel. Clicking an angle opens every competitor ad in it plus every creative of ours descended from it plus that lineage's performance. This screen is the product.

Studio · our creatives

Tabbed by status: candidates, approved, drafted, live, killed. Tabs also by platform and format. Every card carries a free-text revision field, and a revision request re-runs only the relevant stage rather than the whole pipeline: copy edits re-render, concept edits regenerate, video-brief edits rewrite the brief. Batch select, batch approve, batch push.

Compare

Our live ads beside competitor ads in the same angle. Left column our creative with its real metrics, right column the competitor creative with its persistence signals, labelled clearly as inference not measurement so nobody reads a Persistence Score as a CTR. Below, the angle-level verdict: our CPA on this angle against our own portfolio median.

Performance

Meta and Google side by side but never summed, since the metrics are not comparable. Spend, impressions, CTR, CPC, CPA, ROAS where available. Then the cuts that actually inform creative: by angle, by format, by hook, by visual world, by aspect ratio. Plus the fatigue view: frequency against CTR decay slope per ad, with a projected kill date.

Brands

Tracked list with tier, plus a field to drop in any brand name, domain or page id for an immediate one-off sweep, and a promote-to-tracked button.

12Metrics catalogue

You asked what else is worth tracking. The list below is deliberately longer than v1 needs. Mark the starred ones for phase one and let the rest wait.

Per competitor, weekly

Cross competitor

Our side

13Alerts

Push, not pull. Weekly digest by default, immediate for the sharp ones.

14The chat console

One text box on the home page, an agent loop with tools over the same D1 and R2. It should cover three request classes, and it is worth being explicit about them because they need different handling:

  1. Questions. "What is our CPA on the pricing angle this month." Answered from the database, with the query shown so the number is auditable.
  2. Actions. "Sweep Klaviyo and Omnisend for BFCM and make me five statics on their strongest offer angle." Enqueues real work, returns a job the user can watch, and lands the output in the gallery.
  3. Judgment. "Should we go after the deliverability angle." Answered with evidence from the corpus and an explicit recommendation, not a list of considerations.

Two design rules. First, every answer that contains a number links to the rows behind it, or the console becomes untrustworthy the first time it is subtly wrong. Second, any action that would write to Meta or Google returns a confirmation card rather than doing it, no exceptions, even when the request sounds unambiguous.

15Stack and build plan

Orchestration
Cloudflare Workers with Cron Triggers and Queues, matching the stack already in production elsewhere
State
D1 for metadata, R2 for creatives and thumbnails, Durable Object for console session state
Data access
treg for everything external, one token, tagged calls for cost attribution
Generation
MagicFit API for statics, treg or Higgsfield for video
Dashboard
Cloudflare Pages SPA against the Worker API
Long jobs
Queue consumers, never inline in a request, because a full sweep plus decompose will exceed any request budget

Phases

The gates matter more than the phases. Each is a real kill criterion, not a checkpoint to wave through. Phases 1 onward deploy to Cloudflare as they land, so the system is on cloud from the first build phase. Phase 0 is analysis only and writes no code.

PhaseShipsGate to pass
0 · Taxonomy shippedHand sweep of all ten competitors. Angle taxonomy and seed format bank written by hand and reviewed. No code, no infrastructure.The taxonomy holds when applied to a brand that was not used to build it.
1 · Intelligence shippedCloudflare stack stood up. Ingest, decompose, Persistence Score, clustering, weekly cron. Radar and Angles screens. Google read-only via Transparency.The angle inventory changes a brief that would otherwise have been written differently. If it does not, stop here.
2 · ProductionThe six-stage teardown method. Studio gallery with the revision field. Statics end to end, video briefs, Google text ads. Export only, nothing touches a platform.Six of ten candidates in a batch are ones worth running.
3 · Loop closesPaused drafts pushed to the PushOwl account. Daily Meta insight ingest across both accounts. The angle join. Compare and Performance screens.Draft to live conversion above fifty percent. Below that the system is generating work, not saving it.
4 · GoogleOur own Google metrics, CampaignDraft and paused responsive search ad push, real-time policy verdict surfaced before human review.Parity with the Meta path.
5 · Console and alertsChat console over the same data, the seven alerts, whitespace mining from reviews and communities.Every console answer traceable to the rows behind it.

16Risks and open questions

RiskMitigation
Our Google Ads account is not connected yet, so neither our own Google metrics nor Google drafting can workOne browser OAuth on the treg team. Until then Google is read-only competitor intelligence and the Compare screen is Meta only. Worth doing first because it is cheap and unblocks a whole phase.
Meta ad library scrapers break, which they periodically doThree routes already verified. Health-check each on every sweep and fail over rather than failing.
Persistence Score gets read as a performance metricLabel it as inference everywhere in the UI. Never put it in the same visual column as a real CTR.
Copying angles that do not fit our funnelICP fit filter in the Adapt loop, and an explicit kill rule on every adapted test rather than treating a copied angle as proven.
Legal exposure on comparative claimsCompetitor naming off by default, separate legal gate, claims ledger enforced by the validator.
Generated volume outruns human review, and the gallery becomes a graveyardCap candidates per week. Track draft-to-live conversion as a first-class metric. If it falls, generate less, not more.
Cost drift on decompose, since vision passes over thousands of creatives are not freeDecompose once per creative ever, cheapest model that holds schema, tag every treg call for per-loop cost attribution.

Open questions

The four questions this note originally raised are all settled, in the decisions table in section 02. What remains open:

  1. Whether the free ad-library routes return delivery start date and active status at the quality the Persistence Score needs. Tested in Phase 0, and the answer decides the sourcing budget for the whole system.
  2. Whether to give the agent's drafts a dedicated campaign structure inside the PushOwl account or a dedicated ad account, which matters for learning-phase contamination once volume rises. Not urgent while volume is low.