← Realize PluginCONTENT HISTORY
WHAT CHANGED
Update to Realize Plugin
Snapshot Nov 10, 2026 · 00:10 UTC · version 0.5.5
Collection source: downloaded plugin package.
First saved snapshot
No earlier snapshot is available to establish a change.
Save Realize Plugin and check future changes in Following.
Up to 3 plugins free · no card required.
Compare saved observations
Download comparison JSONFull technical diff · 0 changed fields
Full snapshot data
{
"description": "Diagnose a Realize campaign that's underperforming or had a discrete performance event, and prescribe fixes. Walks 5 mandatory pre-checks then routes to RCA (when something broke on a specific date) or general optimization (ongoing review). Activates on questions like \"why is this campaign underperforming?\", \"CPA spiked yesterday\", \"what should I pause?\", \"how do I improve CPA?\", \"campaign isn't spending\", \"lead quality is bad\", \"performance has plateaued\".",
"included_files": [
{
"relative_path": "references/optimization-flow.md",
"size_in_bytes": 28931
}
],
"name": "optimize-campaign",
"skill_md_contents": "---\nname: optimize-campaign\ndescription: Diagnose a Realize campaign that's underperforming or had a discrete performance event, and prescribe fixes. Walks 5 mandatory pre-checks then routes to RCA (when something broke on a specific date) or general optimization (ongoing review). Activates on questions like \"why is this campaign underperforming?\", \"CPA spiked yesterday\", \"what should I pause?\", \"how do I improve CPA?\", \"campaign isn't spending\", \"lead quality is bad\", \"performance has plateaued\".\nallowed-tools: [\"Read\", \"AskUserQuestion\"]\n---\n\n# Optimize Campaign\n\nData-driven diagnosis and recommendation loop for a Realize campaign. Pulls performance data via the MCP, applies mandatory pre-checks, then routes to one of two diagnostic paths and prescribes concrete next actions.\n\n## Internal vs user-facing terminology — never leak the framework names\n\nThe framework labels in this file (*\"pre-checks P1–P5\"*, *\"RCA — 6-signal framework\"*, *\"Signal 1 / Signal 2 / Signal 6\"*, *\"residual signal\"*, *\"creative restructure shock\"*) are **internal scaffolding** for the model. **Never surface them in user-facing output.** Per `os/guardrails.md`, do the work and describe the actual finding in plain English.\n\n| Internal label (this file uses) | What to say to the user |\n|---|---|\n| *\"mandatory pre-checks\"* / *\"running P1–P5\"* | Just do the checks silently. If a check matters to the answer, describe the finding (\"the campaign has no conversion goal attached\"). |\n| *\"RCA — root cause analysis\"* (without expansion) | Either say *\"root cause analysis\"* in full, or just present the analysis. Don't label the process. |\n| *\"Signal 1 (config change) → Signal 2 (supply concentration) chain\"* | Describe the actual sequence: *\"The Feb 13 block redirected spend to Yahoo Homepage, which then served lower-engagement slots — that's what shows up as the CTR drop.\"* |\n| *\"silent failure mode\"* / *\"silent diagnostic-quality killer\"* | Describe what's wrong factually: *\"The campaign doesn't have a conversion goal attached, so the optimizer doesn't know what to bid toward.\"* No \"silent\" anything. |\n| *\"residual signal — never the default\"* | Don't surface the framework hierarchy. Just present the conclusion. |\n\nThe framework helps you think; the user needs the conclusion, not the framework.\n\n**Depth:** The substantive depth (dimensional drill-down rules, supply-side eligibility, creative-fatigue tiers, bid-lever matrix, symptom-class branches, common-mistake table, output discipline) lives in `references/optimization-flow.md`. Read it when a branch fires that needs operational specifics.\n\n## When to use\n\nTrigger on any of these — across the two paths below:\n\n**RCA (something broke on a specific date)**\n- \"CPA spiked yesterday\" / \"conversions dropped this week\" / \"it broke on date X\"\n- \"Why isn't my campaign spending today / since [date]?\"\n- \"What happened to the campaign after I made [change]?\"\n\n**General optimization (no specific event)**\n- \"Why is this campaign underperforming?\" / \"How do I improve CPA?\"\n- \"What should I pause / cut / scale up?\"\n- \"Performance has plateaued — what's next?\"\n- \"Lead quality is bad\" / \"Which sites should I block?\"\n\nIf the user asks to *create* a new campaign — or to apply any of the prescriptions below (pause an item, bump a budget, change a bid, edit targeting) — route to the `manage-campaigns` skill, which calls the MCP write tools behind a preview-then-confirm gate.\n\n## Prerequisites\n\n- `account_id` resolved via the `accounts` skill.\n- A specific `campaign_id` in scope, or the user asking \"across my whole account\". For single-campaign questions, identify the campaign first via the `campaigns` skill if needed.\n\n## Pre-checks (mandatory — never skip)\n\nBefore any analysis, run all five. Skipping leads to wrong conclusions.\n\n| # | Pre-check | Action | Why |\n|---|---|---|---|\n| **P1** | **Tracking health** | Pull a campaign-grain report over the date window with clicks + conversions. On a PARTNER / NETWORK account that is `get_dynamic_report_settings` first, then `get_dynamic_report_data`; **on a GROUP / admin-network account the dynamic tools 403 — use `get_campaign_breakdown_report` instead, which is not an exception to this pre-check but the way to satisfy it there.** Confirm conversions > 0 if clicks > 0. A 403 from the dynamic tools is an account-type signal, never evidence of broken tracking. If tracking looks broken, hand off to `diagnose-tracking` — it verifies the pixel from the user's page and captured browser evidence and routes the fix. | A broken pixel makes every downstream metric meaningless. |\n| **P2** | **Campaign-level conversion goal verified** | Use `get_campaign` to read the campaign's `conversion_rules` / goal mapping. If no campaign-level goal is set, the campaign is **inheriting the account default** — the optimizer targets the most frequent conversion marked \"include in total\" at the account level. This is a **valid configuration when the include-in-total settings are sensible**, not a failure mode. Surface to the user what the campaign is actually optimizing toward (read it from the account's conversion-rules list) — don't assume the KPI from spend volume. | Wrong KPI = wrong diagnosis. Account-default routing is fine when configured deliberately; only flag it as a problem if the account's include-in-total events don't match what the user is trying to optimize for. |\n| **P3** | **Active conversion events only** | Read rule statuses via `get_conversion_rules`; when per-event detail matters, use the metamodel's per-rule conversion metrics and cross off rules whose status ≠ ACTIVE — the dynamic report's aggregate conversions metric is server-computed and cannot exclude dead rules itself. **On a GROUP / admin-network account the metamodel is unavailable (403), so per-rule metrics cannot be pulled at all** — read rule statuses from `get_conversion_rules` (which works there), state that per-event attribution could not be separated, and treat any conversion figure as covering all rules including inactive ones. | Dead events inflate the apparent drop and produce phantom CPA values. |\n| **P4** | **Bid-strategy cross-check** | Read the campaign's `bid_strategy` + `pricing_model`. Map every proposed action against the bid-lever matrix in `references/optimization-flow.md`. If the action is not valid for the strategy, reframe before delivering. | Per-publisher bid moves don't exist on Maximize Conversions / Target CPA / Maximize Value. Per-item bids don't exist on any strategy. Recommending them is a credibility failure. |\n| **P5** | **Learning-period guard** | **Read the campaign's learning status from `get_campaign` — do not count days.** See \"When P5 fires\" below. | The Realize algorithm hasn't stabilised yet — recommendations made during learning reset the timer. Learning length varies per campaign, so elapsed time does not answer this. |\n\nAlongside P1–P5, the **data-sufficiency gates** in `references/optimization-flow.md` apply before any prescription: the statistical-volume tiers, the site-block threshold, and the **daily-spend floor (daily budget ≥ 8× the CPA goal — below it, prescribe a budget raise, not lever tuning)**. **That raise is subject to P5:** prescribe it freely when the guard does not fire, and while it *is* firing only under Exception A (`CVR_LEARNING_LIMITED`). On a plain `CVR_LEARNING` campaign an undersized budget is reported, not acted on.\n\n### When P5 fires\n\n**Learning is a status the campaign reports, not a number of days you count.** One campaign leaves learning in 5 days, another in 12, another never (it stays volume-starved). `get_campaign` returns the status directly — read it.\n\n**Step 0 — approval first, before any learning question.** Read `approval_state` from `get_campaign`. **If the field is missing or `null`, treat it as no blocker and continue to Step 1** — a rendered payload routinely omits nulls, and halting on an absent field would invent an approval problem and shut down the whole optimization path. If it is *present* and reads **`PENDING_APPROVAL` or `REJECTED`**, **stop here** regardless of delivery — a campaign rejected mid-flight is still `is_active` and still shows spend in a trailing window, so delivery proves nothing about these two. Explain the approval state and stop.\n\nThe delivery cross-check applies **only to a value you do not recognise** (something other than `APPROVED`, `PENDING_APPROVAL` or `REJECTED` — the value set is not documented upstream, so one will eventually appear). There, if the campaign is `is_active` with non-zero spend in the window, it is demonstrably serving: note the unfamiliar value and continue to Step 1 rather than refusing a legitimate question. If it is not serving, stop.\n\nWhen you do stop: the blocker is approval, not learning. Do not label the campaign \"Learning period\" and do not tell the user to wait — on a rejected campaign waiting never resolves it. Explain the approval state and stop.\n\nThis has to come before Step 1, not inside it: an unapproved campaign can still report `CVR_LEARNING`, and a status-keyed table would match that row first and give exactly the wrong answer. (`approval_state` is verified present on a live campaign payload, carrying `APPROVED`; the full value set is not documented upstream, so test for *not* `APPROVED` rather than matching a list. It is distinct from `status` / `is_active`, which are the run/pause state.)\n\n**Step 1 — read `cvr_learning_status` from `get_campaign`. This is the primary signal.** (Only reached once Step 0 passes.)\n\n| `cvr_learning_status` | Guard |\n|---|---|\n| `CVR_LEARNING` | **FIRES** — still learning |\n| `CVR_LEARNING_LIMITED` | **FIRES** — learning, and short of conversion volume |\n| `CVR_LEARNING_COMPLETE` | Does not fire — **but only when no Target CPA is set.** With a Target CPA, CVR learning completing does not settle the campaign; go on to Step 2, which must read `LEARNING_COMPLETED` before the guard clears. |\n| Missing or `null`, on a **Fixed-bid** campaign (`bid_strategy` = `FIXED`) | **Not applicable** — no algorithm, so no learning phase. Skip P5. |\n| Missing or `null`, on a **non-conversion objective** (`marketing_objective` is `BRAND_AWARENESS` or `DRIVE_WEBSITE_TRAFFIC`) | **Not applicable** — the campaign does not optimize toward conversions, so this field stays empty for its whole life. Skip P5. Firing here would make the skill permanently unable to advise an awareness or traffic campaign. **This is about the objective, not about whether a campaign-level goal is set:** per P2, a `LEADS_GENERATION` / `ONLINE_PURCHASES` campaign with no campaign-level goal is inheriting the account default, which is a normal setup — it is still learning toward that goal, so it does **not** land here. |\n| Missing or `null`, on a performance campaign where **the account has no conversion rules at all** (the platform allows this) | **Not applicable** — there is no conversion signal for the algorithm to learn from, so this field stays empty for the campaign's whole life. Skip P5 and say what is actually missing: a conversion rule. **Disambiguate from the row above with an account-level check:** an empty `conversion_rules` on the campaign is *not* enough, because per P2 that normally means the campaign inherits the account default. Call `get_conversion_rules` — if the **account** has rules, the campaign is inheriting one, so this row does **not** apply and the final catch-all row below does: **the guard FIRES.** Only when the account has no rules at all does this row apply. |\n| Missing or `null`, any other campaign — **including `MOBILE_APP_INSTALL`** (installs come back through an MMP/SDK as an install conversion rule, which *is* a signal the algorithm learns from, so these campaigns do report CVR learning) — with a conversion objective, non-Fixed, approved, and the **account** has at least one conversion rule — whether attached to this campaign or inherited as the account default) | **FIRES** — treat an absent status as `CVR_LEARNING`, never as \"done\". **This is the catch-all**: if no row above matched, you land here. |\n\n**\"Not applicable\" is not \"mature.\"** When P5 is skipped, the data-sufficiency gates still apply — a day-1 Fixed-bid or awareness campaign is still too thin to judge. Skipping P5 only means the *algorithmic learning* question doesn't arise.\n\n**Step 2 — if the campaign has a Target CPA, also read `target_cpa_learning_status`.** It runs its own sequence on top of the CVR one: `NEW → CVR_LEARNING → CVR_LEARNING_LIMITED → TCPA_LEARNING → LEARNING_COMPLETED`. The guard **fires on every value except `LEARNING_COMPLETED`**. A campaign can be `CVR_LEARNING_COMPLETE` and still be in `TCPA_LEARNING` — Target CPA is not settled until this field says `LEARNING_COMPLETED`. It is `null` when no Target CPA is set — ignore it then. But if a Target CPA **is** set and this field comes back `null`, the guard **FIRES**: an absent status is never evidence that the target has settled, the same rule as Step 1.\n\n**Step 3 — `learning_state` is weak corroboration only. Never decide on it alone.**\n\n| `learning_state` | Read it as |\n|---|---|\n| `LEARNING` / `LEARNING_LIMITED` | Still learning — corroborates Step 1 |\n| `EMPTY_DISPLAY` | **UNKNOWN. Carries no information.** Upstream collapses two opposite states into this one label — a campaign that has *finished* learning and one that has *never served* both report `EMPTY_DISPLAY`. Never read it as \"done\", never read it as \"no creatives\", never mention it to the user. Fall back to Step 1. |\n| `null` | Unknown — fall back to Step 1. |\n\n**Step 4 — there is no estimate path, and that is deliberate.** Step 1's rows cover every combination, including a payload with no learning fields at all: absent and `null` are indistinguishable once a payload is rendered, so both land on the catch-all and the guard **fires**. An earlier draft estimated from conversion count and launch date here; it was unreachable (Step 1 decides first) and it reintroduced the day-counting it replaced. Firing is the safe default — Exception B below is the escape when a campaign stays there too long.\n\n**If `get_campaign` itself failed, you cannot run P5 at all.** Say the campaign could not be read, and do not prescribe from report data alone.\n\n**What changed and why it matters:** the previous rule required the campaign to have been *created within the last 7 days*, joined by AND. That meant no campaign older than a week could ever trigger the guard, whatever its real state. Campaigns commonly report `LEARNING` for months. Age is now irrelevant to the decision.\n\nIf the guard fires:\n\n- **Label** the campaign as **\"Learning period\"** — never \"Underperforming,\" \"Failed,\" or \"Bad performance.\"\n- **Do NOT recommend** bid changes, Target CPA changes, or daily-cap changes — **except** the daily-budget raise permitted by Exception A below, which is the one prescription that ends the learning this guard is protecting.\n- **Do NOT use** the campaign's metrics in cross-campaign benchmarks or reallocation math.\n- **Acceptable actions:** Hold (do nothing), Pause (only if account-wide damage is severe), or Wait.\n- **Re-evaluate** when the status changes — `cvr_learning_status` reaching `CVR_LEARNING_COMPLETE` (and, with a Target CPA, `target_cpa_learning_status` reaching `LEARNING_COMPLETED`). Re-read it; do not wait out a fixed number of days and assume.\n\n#### The two exceptions — a firing guard must not trap a campaign that is stuck\n\nHolding is right for a campaign that is *progressing* through learning. It is wrong for one that cannot progress, and a status-driven guard with no exit would leave a low-volume campaign waiting forever on a condition it can never meet.\n\n**Exception A — `CVR_LEARNING_LIMITED` with an undersized budget.** `_LIMITED` means precisely *not enough conversion volume*. If the daily budget is also below **8× the CPA goal**, the budget is the cause and raising it is the remedy — the one prescription that ends the learning the guard is protecting. **Recommend the budget raise**, sized to clear the 8× floor in one move. State that it is being made during learning and will restart calibration, and make no other change in the same breath — no bid, no Target CPA, no targeting. **This deliberately overrides the usual \"no more than 20% per change\" rule**, and the reason is the same one that exempts correcting a mistyped budget: that rule exists to stop repeated tuning churn re-pacing the algorithm, whereas this is a single structural correction to a budget that was never large enough to produce signal. Walking up in 20% steps would re-pace the campaign several times over and prolong exactly the learning this is meant to end. Make it once. (If the budget already clears 8× CPA and spend is tracking it, the constraint is not budget; hold and say what is actually short.)\n\n**Exception B — stuck past the usual window.** If the campaign has been **treated as learning** — whether from a reported status or from the catch-all row — for materially longer than the ~7-10 day expectation (use **~14 days** as the trigger) and conversion volume is not climbing, say so plainly and **offer the user the choice** rather than holding silently:\n\n> \"This campaign has been in learning for N days, which is longer than usual, and conversions aren't building. Two options: keep waiting, or intervene now — raising the budget or widening targeting gives the algorithm more to learn from, but it restarts calibration. Which would you prefer?\"\n\n**Measure the ~14 days from when the current learning cycle began, not from `start_date`.** No field reports that, so use the best available anchor in this order: **the relaunch date whenever you know one** (not only when the 7-day relaunch override applies — a campaign relaunched 20 days ago is on day 20 of this cycle, not day 300 of its life), then the conversion-goal change date, then `start_date`. **A campaign inside a fresh relaunch cycle does not qualify** — relaunched three days ago on an old `start_date` is on day 3, not day 300, and offering an intervention there would be the age-based reasoning this rewrite removes, wearing the opposite sign. If you cannot establish the anchor, say the campaign has been learning \"longer than expected\" without asserting a count.\n\nDo not apply an intervention under Exception B without the user choosing it. Both exceptions are deliberate departures from Hold/Wait and must be labelled as such when you present them.\n\n**Message the user in plain language first** (before the operator-facing label):\n\n> \"This campaign is still in its learning phase — Realize's algorithm is working out which audiences, sites, and times convert best for you. CPA often looks high while that's happening, because the algorithm is testing. Making changes now (pausing, adjusting budget, swapping creatives) restarts that process. The recommended action is to **wait**. Typically it takes around 7–10 days, but it varies by campaign — some settle faster, some need longer, and it depends on how many conversions are coming in. I'll know it's finished when the campaign reports that it's out of learning, not when a particular day passes.\"\n\nGive the day range as an **expectation**, never as the test. If the user asks \"so is it done on day 10?\", the honest answer is no: it is done when the status says so.\n\n**Special learning cases** — the reported status can lag reality in both directions:\n\n*Overrides a `COMPLETE` reading — the guard DOES fire:*\n- A campaign re-launched **within the last 7 days** after > 14 days of no spend is effectively learning again — treat as Learning even if `start_date` is old and the status says complete. The 7-day bound matters: without it, a pause a year ago would override a `CVR_LEARNING_COMPLETE` reading forever, which is the override direction this guard is built to distrust.\n- A conversion-goal swap restarts learning even on a mature campaign.\n\n*Not an override:* a duplicate of an existing campaign is often said to inherit its source's learning within ~24h. **Do not use that to overrule a `CVR_LEARNING` reading.** Nothing in the MCP marks a campaign as a duplicate, so it would rest entirely on the user's say-so, and overruling the reported status on an age basis is the exact failure this guard was rewritten to remove. If the duplicate really has inherited learning, the status will say so.\n\n**Exit criteria from pre-checks:** Do not advance past the pre-checks if the campaign is blocked on approval (P5 Step 0 — explain the approval state and stop), tracking is broken (P1 — route to `diagnose-tracking` first), or the campaign is in Learning period (P5, subject to its two exceptions).\n\n## Two paths — pick by user framing\n\n| Use this path when | Framework |\n|---|---|\n| User reports a discrete event: \"it broke on date X\" / \"CPA spiked yesterday\" / \"conversions dropped this week\" | **RCA — 6-signal framework** (below) |\n| Ongoing review, no specific drop: \"optimize this campaign\" / \"what should I change\" / \"performance has plateaued\" | **General optimization — 7-point framework** (below) |\n\nBoth paths share the dimensional drill-down, supply-side eligibility check, and creative-fatigue check — all in `references/optimization-flow.md`.\n\n## RCA path — 6-signal framework\n\nWhen the user reports a discrete drop, walk these 6 signals in order. The first match usually wins. Run signals 1-5 in parallel against the same campaign; signal 6 is a coordinated-restructure check that runs alongside.\n\n| # | Signal | What to check | Evidence pattern |\n|---|---|---|---|\n| 1 | **Config change** | Campaign / targeting / tracking changelog over the 7 days before the drop — pull it with `get_campaign_history_report` (the change/audit log). **If the log comes back empty or doesn't cover the change class in question, fall back to asking the user directly:** *\"In the 7 days before the drop, did you change anything on the campaign — budget, bid strategy, targeting, blocks, creatives, conversion event? If yes, what and when?\"* Use the answer as Signal 1 input. **Do NOT silently skip Signal 1.** | A change within 3 days of the drop = candidate. |\n| 2 | **Supply shift** | Pre-drop vs post-drop publisher mix. Check for: top publisher losing ≥ 5% share, new competitive-filter / brand-safety blocks appearing, publishers manually blocked / paused. | Any top-3 publisher losing ≥ 10% combined share, or a CF block appearing for the first time. |\n| 3 | **Creative fatigue / issue** | Top-5 active ads ranked by spend, CTR week-over-week. Frequency is **UI-sourced** — not in the MCP metric surface; ask the user for it rather than inventing it. **Also check paused ads** — a top performer that was paused looks identical to fatigue in the aggregate. | Frequency > 4 (user-supplied), CTR decay ≥ 30% from peak, or ad status changed to PAUSED. |\n| 4 | **Tracking break** | Attributed-conversions trend from the dynamic report (per-rule conversion metrics where they matter), 14 days before drop vs after, with clicks alongside. No MCP tool exposes fired/matched ratios — a break here can't be localized from reports alone, so a suspicious shift hands off to `diagnose-tracking`. | Attributed conversions down > 20% while clicks held steady. |\n| 5 | **External / algo lifecycle** | Bid-strategy state — is the algo in learning, stable, underfunded? Auction insights. Market seasonality. | No config / supply / creative / tracking change AND the algo state matches a known lifecycle phase. **Residual signal — never the default.** |\n| 6 | **Creative restructure shock** | Cluster item-level changes by hour, from the change log (`get_campaign_history_report`) where it surfaces item-level changes — verify against real output; where it doesn't, ask the user what was restructured and when. If ≥ 3 distinct campaigns had item-level changes in the same hour AND a CPA / ROAS break followed within 48h, flag as restructure shock. | Coordinated pause-old + launch-new on the same day → 24-48h CPA break, 5-7 day recovery. |\n\n**Golden rule:** Only conclude \"external / algo\" AFTER ruling out signals 1-4 and 6. \"External\" is the residual, never the default.\n\n**Ranking candidates within a signal:** each candidate has **weight × behavioural difference**. Weight = share of spend before the drop. Behavioural difference = how much this item diverged from campaign average. A 3%-share publisher with a 50% CVR delta is **not** the driver. A 25%-share publisher with a 30% CVR delta **is** the driver.\n\n**Output shape (RCA):** the single strongest story, not a laundry list.\n\n```\nROOT CAUSE: Publisher <name> (ID <id>) dropped from <X>% → <Y>% of spend\nstarting <date>, after <triggering event>. Post-event CPA rose from $<A> → $<B>\nbecause spend redistributed to <N> lower-CVR publishers. No other changes detected.\n```\n\nThen **max 2 actions**: reverse the change (if reversible) or work around it (duplicate / expand supply / add creative).\n\n## Optimization path — 7-point framework\n\nWhen the user asks for an ongoing optimisation (no specific drop), walk this in order. Most issues resolve by point 4.\n\n| # | Principle | Action | Read |\n|---|---|---|---|\n| 1 | **Average = outliers** | Identify outlier segments via the dimensional drill-down in `references/optimization-flow.md`. Consider excluding the worst. | `knowledge/site-management.md`, `knowledge/custom-rules.md` |\n| 2 | **Don't be hasty** | Apply the data-sufficiency thresholds in `references/optimization-flow.md`. Use longer lookback windows. | `knowledge/custom-rules.md` |\n| 3 | **Leverage tools** | SpendGuard (automated, on by default), Custom Rules (semi-automated — only AFTER the campaign has finished the learning phase; for newer advertisers, monitor 1-2 weeks before any rule), conditional filters (manual). | `knowledge/custom-rules.md` |\n| 4 | **Tap into new supply** | Mail, predictive audiences, retargeting, Apple News, Lockscreen. | `knowledge/environments.md`, `knowledge/targeting.md` |\n| 5 | **Refresh creatives** | Apply the fatigue tier check in `references/optimization-flow.md`. Refresh = new angle, not a word change. | `knowledge/creative.md` |\n| 6 | **Check data volume** | Add a secondary conversion event if the signal is thin. | `knowledge/tracking.md` |\n| 7 | **Scale top performers** | Pull the account-average CPA down by adding good-CPA volume. | `knowledge/budget.md` |\n\n### Symptom-class branches\n\nAfter the 7-point walk, the symptom usually maps to one of these branches — full text in `references/optimization-flow.md`:\n\n| Symptom | Depth-file branch |\n|---|---|\n| CPA fluctuating (not a single drop) | §7.1 CPA fluctuating — by bid strategy |\n| Low CVR (CPA acceptable) | §7.2 Low CVR (CPA acceptable) |\n| Underspending / not delivering | §7.3 Underspending / not delivering |\n| Lead quality bad (performance acceptable) | §7.4 Lead quality |\n| Plateau / gradual decline | §7.5 Plateau |\n| CPA + CVR both underperforming | §7.6 Combined (CPA + CVR both underperforming) |\n\n## Common action prescriptions\n\nFor each prescription below, hand off to `manage-campaigns` for application (it owns the MCP write tools and the preview-then-confirm gate). After the user confirms the write completed, re-verify via the MCP read tools (`get_campaign` / `list_items` / `get_campaign_history_report`) and re-pull the relevant report after a data window (typically 3–7 days, accounting for the change-class lag in `references/optimization-flow.md`). For prescriptions still UI-only (Custom Rules, audience uploads, pixel installation, codeless-conversion setup, pixel test-fire), `manage-campaigns` walks the user through the Realize UI fallback. **Conversion-rule changes are no longer UI-only** — creating a rule, adjusting an attribution window, or retiring a rule are MCP writes that go through the same gate. **Pixel-health diagnosis is no longer UI-only either** — when the prescription is \"figure out why the pixel/conversions are broken\", hand off to `diagnose-tracking`.\n\n- **Pause an underperforming item** — MCP write via `manage-campaigns` (`update_native_item` / `update_display_item`, `is_active=false`).\n- **Block a site** — MCP write via `manage-campaigns` (merge into `publisher_targeting` EXCLUDE — full-replace semantics, so read-and-merge). Run the historical-top-N guard (in `knowledge/site-management.md`) before recommending.\n- **Adjust daily budget** — MCP write via `manage-campaigns` (`update_campaign`, `daily_cap`). Per-change cap: 20%. Per cadence: every 2-3 days.\n- **Add a Target CPA** — MCP write via `manage-campaigns` (`update_campaign`) — but **last-resort lever only** (see `knowledge/bidding.md`, including the bid-strategy-switch rules). Set within 10-20% of stable CPA after 3-4 days of delivery — never at launch, never aspirational.\n- **Refresh creatives significantly** — MCP write via `manage-campaigns` (`create_native_item` / `create_display_item`). Recommended: 3 distinct titles + 3 unique images per campaign; 4–6 items per campaign for algorithm testing, never more than 10.\n- **Tighten / broaden targeting** — MCP write via `manage-campaigns` (`update_campaign` targeting blocks — full-replace within a section, so read-and-merge).\n- **Isolate a top performer** — MCP writes via `manage-campaigns` (`create_campaign` + item creates) containing only the winning item(s) at a fresh learning state.\n\n## Output discipline — the two gates\n\n**Gate 1 — Chat output by default.** This skill answers in the terminal/chat. Concrete, scannable, lead with the action.\n\n**Gate 2 — Actions-only filter.** Output lists ACTIONS the user should take — not a narrative of what was analysed. Data and context appear ONLY when they explain *why* an action was recommended. Every bullet must answer: *\"What do I do differently because of this?\"* If a line doesn't recommend a change, it doesn't belong in the output.\n\n## Hand-off to the depth file\n\nRead `references/optimization-flow.md` when:\n\n- A dimensional drill-down or supply-side eligibility check is needed (statistical-volume tiers, auction insights, blockers, narrow-targeting routing, concentration risk).\n- A symptom-class branch fires (CPA fluctuation, low CVR, underspending, lead quality, plateau, combined).\n- The bid-lever quick-reference / lag-effect table / signal-interpretation tables / common-mistake patterns are needed.\n\n## Guardrails (high-level — full list in `references/optimization-flow.md`)\n\n- Never skip pre-checks P1-P5.\n- Never label a Learning-period campaign as \"underperforming.\"\n- Never conclude \"external / algo\" without ruling out signals 1-4 + 6 in the RCA path.\n- Never recommend per-item bid changes (don't exist on any strategy) or per-publisher bid moves on Maximize Conversions / Target CPA / Maximize Value (only block / unblock / whitelist).\n- Never recommend a budget reduction *as an optimisation lever* for high CPA. Budget corrections for stated user errors (e.g., \"I set my daily budget to $10,000 by mistake, should be $100\") are fine.\n- Never recommend switching TO Fixed Bid as a \"fix\" — it disables algorithmic optimisation.\n- Never set Target CPA at launch or aspirationally — it is a last-resort lever (see `knowledge/bidding.md`).\n- Never recommend a creative refresh that's a word change — refresh = new angle.\n- Always pair every observation with a concrete action item.\n- Always label the attribution model on every CPA / CVR / Lead / ROAS figure.\n- Always state the source of every claim (which MCP report, which date window, which row count).\n"
}SHA-256 of public snapshot: a17441d76e71ad643567be455c360a567f6962f73214e11cc09593eae367f136