AdCopilot for Claude Code
The AI agent that runs your Google Ads, with Google Analytics, Google Search Console and Google Tag Manager in the same chat. Audit wasted spend, mine search terms, build campaigns and fix conversion tracking from Claude Code. AdCopilot proposes each change first. For a hard stop, set your AI client to ask before AdCopilot's write tools: Needs approval in Claude; in Cursor, use the Allowlist run mode and keep them off the allowlist (Cursor's cloud agents never ask). AdCopilot never deletes anything but a negative keyword. This plugin bundles the hosted AdCopilot connector and carries the steps the connector cannot take for you: what to click in Google's own screens, and the traps that are easy to fall into.
AdCopilot is made by Atromx Intelligence (https://adcopilot.cloud) and is not related to other products named AdCopilot.
The plugin is free (MIT). The connector is a hosted service — plans at https://adcopilot.cloud/pricing.
Install
Two steps on every surface: add the plugin, then sign in to the connector it brings with it. The second step is the one people miss, and nothing works until it is done.
AdCopilot connects to Google Ads through its own Google Cloud project, with your Google sign-in. If you want programmatic access to Google Ads for your own software, get your own Google Cloud account and apply for Google Ads API access there.
claude.ai, Cowork and the desktop app
- Add the plugin: open AdCopilot in the plugin directory, or go to Customize, then Plugins, and search for AdCopilot. Click Add.
- In the plugin, open its Connectors tab, click Connect beside
adcopilot, and sign in.
The connector is already listed — there is nothing to add, and no need for "Add custom connector" or the connector URL: the plugin registers it for you. If you added AdCopilot by hand before installing the plugin, nothing breaks: Claude links a connector by its URL, so you still have one AdCopilot connector, the same one.
If you clicked Try in Cowork straight from the listing, you landed in a chat before step 2 — the connector will report as missing. Do step 2 and say "get me started" again.
Claude Code
/plugin marketplace add AtromxIntelligence/adcopilot-claude-plugin
/plugin install adcopilot@adcopilot-claude-plugin
/mcp
The last step opens the connector list. Choose adcopilot and sign in with your own Google account. There is no claude mcp add step: the plugin registers the connector at https://mcp.adcopilot.cloud/mcp for you. Signing in links your AdCopilot workspace, so you need an AdCopilot account; your Google products are connected once, in AdCopilot at https://app.adcopilot.cloud, not in Claude Code — /adcopilot:setup tells you which are missing and sends you to the right page.
Cursor
Cursor takes AdCopilot's connector on its own; this plugin does not install
there as a whole. Its skills are written for Claude, and Cursor reads them too
when they are copied into .cursor/skills or .claude/skills. Wherever they
work out which client they are in (/adcopilot:setup, adcopilot-connect, the
daily-habit offer), they check for Cursor first: there they never run
claude mcp, look for the connector in Cursor's MCP settings and make no
schedule offer of their own; a check-in offer AdCopilot's answer asks for is
made as it asks.
-
Add the connector: Add AdCopilot to Cursor opens Cursor with the connector named
adcopilot. From inside Cursor the same link iscursor://anysphere.cursor-deeplink/mcp/install?name=adcopilot&config=eyJ0eXBlIjoiaHR0cCIsInVybCI6Imh0dHBzOi8vbWNwLmFkY29waWxvdC5jbG91ZC9tY3AifQ%3D%3D. Or put it in~/.cursor/mcp.json(every project) or.cursor/mcp.json(one project):{ "mcpServers": { "adcopilot": { "url": "https://mcp.adcopilot.cloud/mcp" } } } -
Sign in: Cursor opens AdCopilot's page, Let Cursor use your AdCopilot?, in your browser. Press Approve; there is no API key to paste. If Approve did nothing on an earlier try, remove AdCopilot in Cursor's MCP settings, add it again and press Approve once; see Approve does nothing.
-
Ask: "Get me started with AdCopilot".
Added from cursor.directory? Its Add to Cursor button can name the
connector server instead of adcopilot, so Cursor's MCP list and its
approval questions show server. It is the same connector. The link above
names it adcopilot.
Approvals in Cursor. Cursor has no Needs approval setting; its run modes decide, in Settings, then Agents, then Approvals & Execution (at the time of writing). In Allowlist, a tool on the allowlist runs without asking and any other asks first, so keep AdCopilot's write tools off the allowlist. In Auto-review, Cursor's recommended default, allowlisted calls also run at once, and every other MCP call goes to a classifier that can let a change through without asking. Run Everything asks before nothing, and a Cursor cloud agent never asks you to approve an action. Whatever the mode, AdCopilot proposes each change first, never deletes anything but a negative keyword, and builds every campaign paused.
Then, on every surface
Signing in to the connector links your AdCopilot workspace, so you need an AdCopilot account. Your Google products — Google Ads, Analytics, Search Console, Tag Manager — are connected once, inside AdCopilot at https://app.adcopilot.cloud, not in Claude. So a fresh sign-in can report nothing connected: that is the server answering correctly, not a fault.
Run /adcopilot:setup first. It says what is connected, what is not, and sends
you to the right page for each.
If you had already added the connector by hand before installing (Claude Code only — the web has no such command), Claude Code keeps yours and silently ignores the plugin's: claude mcp list then shows a bare adcopilot: line and no plugin:adcopilot:adcopilot line. Remove yours from every scope it is in — claude mcp remove adcopilot -s user, and claude mcp remove adcopilot -s local run from the directory you added it in, because a local registration belongs to that directory — and the plugin's line appears and asks you to sign in.
What you get
Seven commands, which you type:
/adcopilot:setup— checks the connector is connected and signed in, says what each connected product unlocks, and catches the two ways a fresh install goes wrong: a lapsed sign-in, and, in Claude Code, a copy of the connector you added by hand before installing./adcopilot:launch— a first Search campaign, through theadcopilot-launchskill below./adcopilot:measure— conversion tracking, through theadcopilot-measureskill below./adcopilot:daily— the daily check-in: this month's budget pacing, the last seven days' searches that cost money and brought nothing, any ad Google has disapproved or limited, and what changed in the account since yesterday and who changed it — in that order, as far as the reads AdCopilot sizes for one answer allow./adcopilot:weekly— last Monday to Sunday against the week before: spend, conversions and cost per conversion, the three biggest changes, and three things to do this week, written to be forwarded./adcopilot:negatives— the last seven days' searches that cost money and brought nothing, grouped by theme, with the never-show-for words (negative keywords) that would block them, proposed first (the command asks which themes to block) and read back once added./adcopilot:audit— the connector's full audit and this month's budget pacing, with the findings that are only a symptom of a brand-new account kept separate, and the first fix proposed with what it costs to act and to leave it, read back once made, then the next one proposed.
Nine skills, which Claude draws on when the conversation calls for them:
- adcopilot-connect — sets up AdCopilot, connects your Google products and links them to each other in the order that works, saying what each step bought you as it happens.
- adcopilot-measure — sets up conversion tracking end to end: the Analytics property and data stream, the Tag Manager tags, the key event, the Analytics-to-Ads link and the import, and ends with exactly one Primary conversion, proven by a real click.
- adcopilot-launch — builds a first Search campaign switched off, on the budget and bid you name: locations, never-show-for words, ad groups, keywords with their match types, ads and assets; verifies the settings that leak money by reading the campaign back; hands you the go-live switch, and asks you to say when it is on so it can read the first day's delivery; and runs the first week's checks, reading why a switched-on campaign is not delivering before it sends you to any screen.
- adcopilot-daily — the morning check a Claude scheduled task or a Claude Code routine runs while nobody is there: it is written to read only and never ask a question, opens with "All clear" or "Needs a look" and ends with the one fix worth doing, as a yes/no question for when you open it. When you answer yes, it re-reads what the fix touches and states the change again before making it, because the report is hours old by then. It also holds the steps for setting that schedule up.
- adcopilot-review — the everyday one: reads the account and says what happened, what it cost, what is wasting money and what to change, then proposes the change and applies it when you say yes, reads back what the change's own result does not show, and proposes the next fix the same way. It opens with
full_audit, because one lens is one read and answers what a dozen hand-written queries would, and it treats the number of reads AdCopilot sizes for one answer as something to spend rather than stay under, never as a daily limit. - adcopilot-fix-checks — the fixes for the checks an AdCopilot audit flagged, when you send the question from the audit page's or an email's "Fix all three" or "Fix this" button. It says first what the fixes cost and what today's operations cover, takes the checks foundation first (counting results before anything judged by them), reads only what each fix needs rather than running the audit again, and puts each fix to you as one yes/no, with at most five rows of any list. A search-term check goes to the never-show-for words, zero-conversion keywords to a pause, and a conversion-tracking check to the steps AdCopilot can take or the clicks in Google Ads it cannot. Each change is read back, with how to undo it, before the next check.
- adcopilot-docs — answers questions about AdCopilot itself from your live workspace rather than from memory: which products it reaches, how many look-ups are left today and what happens when they run out, which plan you are on, what it is allowed to change, where data goes, and what to do when a call fails. Plans and prices are linked, never quoted. It answers from that one look at your workspace, without reading your accounts or starting another skill, and says what AdCopilot can do as one list. When the question pointed at something a read of your account would answer, it ends on one question offering that read; after a question about look-ups, billing, privacy or safety it offers nothing.
One agent, which Claude hands a job to when it is too big for the conversation:
- adcopilot-auditor — a full account audit end to end, returning findings with the numbers behind them and the changes worth making. It reads and proposes only: it holds no write tool, so it cannot apply anything, and the conversation that asked for it takes your yes.
Together with the connector registration above, the commands, skills and agent above are what the plugin ships at this version.
Every morning, without asking
Every command above, and the connect, launch, measure and fix-checks skills, make one short offer to turn a morning check of the account into a daily habit when AdCopilot's own answer hands them the recipe for your AI client (routine.schedule): an account with a campaign switched on, no schedule reported yet, and a client AdCopilot gives scheduling steps for, which today means Claude. AdCopilot gives none for Cursor, ChatGPT or another client, and there the plugin makes no schedule offer of its own; in Cursor, when AdCopilot's answer asks for an offer of a check-in or a summary, the plugin makes that one as AdCopilot words it. The offer comes, as a paragraph of its own, only in a reply that asks you no yes/no on a fix: the reply that reads back the first fix you approved, where it stands in for the next fix's yes/no (the next fix is named, and proposed in the reply after), or at the close of a reply that proposes none. When that fix was for a finding the morning check also reads (the searches that spent with no sale, a campaign out of budget, a disapproved ad, a drop against the 7-day average, a change nobody asked for, except on Free's 10 a day, where the check leaves what changed for the chat), it is tied to it: a check that runs by itself every weekday at 8:07 would have caught it on its first morning. With any other finding, or none, the offer closes the reply instead, without that claim. In claude.ai, Cowork or the desktop app it is a Claude scheduled task; in Claude Code, a routine made with /schedule, which it offers to create for you. Say "schedule it" and it walks you through the steps; say no and it does not offer again in that conversation, asks once whether your Claude has scheduled tasks, and records the answer, so a no pauses the offer for a week and a Claude with no scheduled tasks ends it. It records that answer only from Claude, because AdCopilot keeps it for the whole workspace. The offer stops for good once AdCopilot reports a schedule — one you recorded with it, or a scheduled run that has checked in on two different days.
The instructions you save are AdCopilot's own morning-check recipe, word for word, filled in with your account. A run reads about six or seven times, and each read counts toward your workspace's daily operations; on a Monday, the daily check and the weekly summary together can use all of a Free workspace's 10. On Free, and on a trial held to Free's 10 a day, an analysis such as budget pacing counts 2 operations and a single read 1, so all five reads would come to about 8 to 10 of Free's 10, some days all of them; there the run makes its first four reads only, about 6, leaving what changed for the chat, so the fix it ends on still fits the same day. On a paid plan or a running trial every read counts 1. If AdCopilot already emails you its daily check-in, that keeps coming as well; the Routine card on AdCopilot's Home page pauses it if you want only one. Scheduled tasks need a Claude plan that has them; on Claude's Free plan, and in Cursor, AdCopilot's emailed daily check-in, when it is on, is the morning check.
A scheduled run is told not to change anything. The instructions adcopilot-daily gives you are read-only and ask no questions; a fix it finds is a question for when you open the report. AdCopilot holds a scheduled run to reads as well: once a run's first call is get_org_context with source: "claude-scheduled", AdCopilot refuses every tool that changes Google Ads, Analytics, Tag Manager or Search Console on that connection for the rest of the run, up to 20 minutes, whatever Claude allows (AdCopilot's own notes aside). It knows a run is scheduled only from that first call, so a run that skips it or sends another source is not held, and a yes typed in the run's own conversation can be refused while the run is on; say it again in a new conversation. Claude Code adds one more hold: AdCopilot marks set_campaign_status, the tool that pauses a campaign or switches one on, as needing a person, so Claude Code (version 2.1.214 or later) will not use it in a run on your computer with nobody there to answer, such as claude -p. The plugin's skills pause or switch on a campaign only with that tool, never with a status in a change to the campaign's settings (update_campaign), and the mark does not hold that change. A Claude Code routine runs its tools without asking, and so does a scheduled task switched to Automatically approve; in those, the first call and the instructions are what keep the run to reads, so keep them as written. In claude.ai, Cowork and the desktop app, setting AdCopilot's other Write/delete tools to Blocked, every one but the two AdCopilot tools below, stops a change outright, in every chat as well as in the run.
The one setting a scheduled task needs in Claude. In claude.ai, Cowork and the desktop app, a run with nobody there stops at the first tool Claude asks about. So the set-up's first step is in AdCopilot's Tool permissions (Customize, then Connectors, then AdCopilot): the Read-only tools on Always allow, and under Write/delete tools two of AdCopilot's own, “AdCopilot: what is connected and where you are” and “AdCopilot: save business notes and progress”, on Always allow too. They keep AdCopilot's own notes and never change Google Ads; every other Write/delete tool stays on Needs approval, so Claude asks before each change (Cursor has no such screen; see Approvals in Cursor above). Claude has listed the first of the two under Write/delete tools since AdCopilot v2.42.3, and it is every run's first call, so a task set up with only the read-only tools on Always allow, as the plugin's steps said up to 0.2.13, stops there; setting those two tools to Always allow is the whole fix.
What it will not do
- Switch on a campaign it built. Every campaign it builds is created paused. You switch it on in Google Ads yourself.
- Delete anything but a negative keyword. The connector refuses a REMOVED status server-side: the campaign, ad group, ad and keyword tools accept only ENABLED or PAUSED, and the asset tools refuse a remove outright. Pausing is as far as it goes for all of those, and it is the server that enforces that rather than the assistant's good manners. The one exception is deliberate: each of the three ways to add a negative keyword has a removal behind it, so a negative can be taken back out — which is what makes "you can undo this" true when AdCopilot says it. Attaching a shared negative-keyword list is the genuinely one-way action; there is no detach tool, and a list is detached in Google Ads under Tools, Shared library, Exclusion lists.
- Change your account unasked, while your AI client asks before AdCopilot's write tools. Reads run freely, and AdCopilot proposes each change first. For a hard stop, set your AI client to ask before AdCopilot's write tools: Needs approval in claude.ai, Cowork and the desktop app; in Claude Code, leave them off the allowlist and the session out of
autoandbypassPermissionsmode; in Cursor, use the Allowlist run mode and keep them off the allowlist (see Approvals in Cursor above; Cursor's cloud agents never ask). Take the question away and changes to what already exists, a budget or a switched-on campaign among them, apply without it; only a new campaign is still built paused. The one exception isset_campaign_status, the tool that pauses a campaign or switches one on: Claude Code (version 2.1.214 or later) asks before every use of it, whatever its settings, and a run with nobody there to answer cannot use it; the Claude desktop app may also ask every time for it. The plugin's skills pause or switch on a campaign only with that tool, never with a status in a change to the campaign's settings (update_campaign), which follows your settings like any other change.
Support
- Something wrong with the plugin (a command, a skill, this README): open an issue here.
- Something wrong with the connector (sign-in, a tool call, your account): [email protected].
AdCopilot version
0.2.16 (2026-10-23, written against AdCopilot v2.44.1, which must be live before this version is published; v2.44.0 was the release in production when this text was written): the release checklist below must be run before this version is tagged, and none of its steps had been run when this text was written, the structural tests and claude plugin validate --strict aside. The listing's name and description now name every Google product people search for. No change to what a customer or the model reads: no skill, command, agent, tool or .mcp.json says or does anything different. In the manifest, the display name becomes "AdCopilot for Google Ads, Google Analytics (GA4), Google Search Console & Google Tag Manager". The description leads with what the agent does, and each claim in it was read against AdCopilot v2.44.1's tools: Keyword Planner data (get_keyword_ideas, get_keyword_metrics, get_keyword_forecast), Search and Performance Max builds (create_campaign, create_performance_max_campaign), the Merchant Center products that cannot show in Shopping (shopping_product_issues, new in v2.44.1), and negative keywords as the only thing AdCopilot removes. It keeps the approval sentence, with its Cursor clause, and the negative-keyword sentence, word for word, because the structural tests require both. The keywords grow from twenty to twenty-eight: keyword-planner, keyword-research, merchant-center, google-shopping, shopping-ads, seo, local-services-ads (lsa_leads and lsa_rate_lead, new in v2.44.1 and on by default) and google-ads-reports. The marker in every file moves to claude-plugin/0.2.16, and so does the plugin.version the eval fixtures' get_org_context answers carry, because the structural tests hold both to the manifest's version; those are the only changes to a skill, command, agent or eval file. No eval case was run for this text: what the model reads is 0.2.15's with the version number moved.
0.2.15 (2026-10-23, written against AdCopilot v2.44.0, deployed 2026-10-22): the release checklist below must be run before this version is tagged, and none of its steps had been run when this text was written, the structural tests and claude plugin validate --strict aside. No change to what a customer or the model reads: no skill, command, agent, tool or .mcp.json says or does anything different, and nothing in the manifest changes but the version. The plugin directory's pre-submission checklist says to keep every file that is not an image or a font under 256 KiB, or the version is held for a reviewer, and to keep the plugin to 512 files or fewer. At 0.2.14 the structural tests were one file, tests/test_structure.py, of 273,469 bytes, 11,325 over that line; the next largest file, the README at 93,719 bytes, was well under it, and the plugin had 242 files. That file is now six files by topic, tests/test_daily_habit.py, test_unattended_run.py, test_manifest_and_marker.py, test_review_and_audit.py, test_handoff_and_approval.py and test_eval_cases.py, the largest 66,136 bytes, with the helpers they share in tests/structure_helpers.py. The tests moved verbatim, the same classes, test names and assertions: 252 tests before and 252 after. The one edit inside them is that the three tests that pin this README's 0.2.14 note now look that note up by its own version, not the manifest's, so a release that changes none of what it pins does not have to repeat it. A new tests/test_plugin_size.py fails, naming the file, on any file that is not an image or a font and is over 250 KiB, or on a plugin of more than 480 files, so this repository now measures the limit itself; the plugin has 249 files. The marker in every file moves to claude-plugin/0.2.15, and so does the plugin.version the eval fixtures' get_org_context answers carry, because the structural tests hold both to the manifest's version; those are the only changes to a skill, command, agent or eval file. No eval case was run for this text: what the model reads is 0.2.14's with the version number moved.
0.2.14 (2026-10-23, written against AdCopilot v2.44.0, deployed 2026-10-22): the release checklist below must be run before this version is tagged, and none of its steps had been run when this text was written. Changes to what the plugin tells the model, one new skill and two new cases, and nothing to the manifest but the version. First, and the reason this version goes out first: a morning check scheduled from the plugin no longer stops at its first call. Since AdCopilot v2.42.3, Claude lists get_org_context, titled “AdCopilot: what is connected and where you are”, under Write/delete tools, and up to 0.2.13 the set-up put only the read-only tools on Always allow, so a run set up that way waited at its first call for an approval nobody was there to give. The daily skill's first set-up step for Claude now says what AdCopilot's own step says (schedule_recipe.ALWAYS_ALLOW_STEP): in Customize, Connectors, AdCopilot, Tool permissions, the Read-only tools on Always allow, and under Write/delete tools “AdCopilot: what is connected and where you are” and “AdCopilot: save business notes and progress” on Always allow too, because they keep AdCopilot's own notes and never change Google Ads; every other Write/delete tool stays on Needs approval, so Claude still asks before each change (Cursor has no such screen; see Approvals in Cursor above). This README's morning section says the same, and the advice to set the other write tools to Blocked leaves those two out, since blocking them would stop every run. The instructions the set-up hands over are AdCopilot v2.44.0's morning-check recipe word for word, and their first call is now: Call get_org_context first, with source "claude-scheduled" and task "AdCopilot morning check". AdCopilot counts only a run whose first call also names that task as its morning check, so a customer's own scheduled task is a scheduled run, not the morning check, and no longer ends the morning-check offer; the daily skill's run sends the task whenever the instructions open with "AdCopilot morning check", even when an older saved copy of the recipe does not name it, and never for a scheduled task worded any other way, and daily-scheduled-run now fails a first call without it. Second (#22), every file that sends the plugin's marker, source set to claude-plugin/0.2.14, now also says, in one sentence that is the same in each, that a scheduled run, started by a Claude scheduled task or a Claude Code routine with nobody there to answer, sends source: "claude-scheduled" instead, never claude-plugin. Before, only the daily skill's run said so, and a scheduled task in the customer's own words, answered by the review skill, reached AdCopilot as a conversation with a person in it. A new case, review-scheduled-unmarked, owned by adcopilot-review, is that run. What this version does not change: the daily skill's run still calls no . Every schedule set up before 0.2.14 keeps that tool asking first, and a scheduled run cannot answer an approval prompt, so a run that kept a note would stop there and never finish. The ban lifts only once AdCopilot has told the people who already schedule the check to set the two tools to , and that notice has been measured. The third to eighth changes (#24) come from one conversation on 2026-10-20, on a workspace with 10 operations a day: the audit page's "Fix all three in Claude" question was answered with three menus of numbered options and up to three related reads per check, the daily-habit offer came after the first of the three checks, and the day's operations ran out before the fix the customer had agreed to could run. Third, a new skill, , for the questions AdCopilot's audit page and emails send: "My AdCopilot audit of Google Ads account … flagged three checks to fix first", or one that "failed" or "warned on" a check, each named with its ID in brackets. It says first what the fixes cost in operations (each check's read, plus one call per keyword a change pauses or per campaign or list it adds never-show-for words to, plus a read-back) and what today covers, recommends the order itself, foundation first (counting results before anything judged by them), reads each check with its own lens and runs no new audit, puts each fix to the customer as one yes/no with at most five rows of any list, reads back every change with how to undo it, and makes the daily-habit offer only once a fix has been applied and read back. A search-term check (G18 and its family) goes to the never-show-for words, zero-conversion keywords (G-WS1) to a pause, and a conversion-tracking check (G43 and its family) to the steps AdCopilot can take, or to the clicks in Google Ads where it cannot make the change. Before a call that would use the last of today's operations, it says what that call covers and asks first. Its case, , is new and had not run when this text was written. Fourth, the daily-habit offer, in all eight files that carry it (fix-checks carries the block itself) and in , is made in a reply that asks the customer no yes/no on a fix: the reply that reads back the first fix they approved, where the offer stands in for the next fix's yes/no (the next fix is named in one line and proposed in the reply after), or the close of a reply that proposes none. A fix for a finding the morning check also reads is still what lets the offer say the check would have caught it; on a workspace with 10 operations a day, whose morning check leaves what changed for the chat, a change nobody asked for gets the close instead. Fifth, one decision per reply. and end on one yes/no on the step they recommend, never a numbered menu or a request to pick the order; they show at most five rows of any list, costliest first, then "and N more"; and in Claude Code they put that yes/no with the AskUserQuestion tool. also says what a call covers and asks first before the one that would use the last of a short day's operations. Sixth, : every surface now runs at most one that bears on the question (it was three), as AdCopilot's own instructions say from v2.43.3. Seventh, no longer carries , so the model can start it when the customer asks to block searches; its description now says checks from an AdCopilot audit are the fix-checks skill's and a question about how the ads are doing is the review's, which is what 0.2.12's flag was for. The fix-checks steps apply its write steps with the search-term read they already hold and do not start it, which would read the last seven days again. Eighth, the unattended morning check, on a workspace with 10 operations a day, makes its first four reads only, leaving what changed for the chat, so the fix its report ends on still fits the same day: at 0.2.13's weights the four count 6 of Free's 10, where the five count 7 and a run about 8 to 10, and the daily skill's run and set-up and this README say so. Ninth (#25, #28), the audit's count. AdCopilot v2.44.0's audit has seventy checks, each listed, and grades its score over the checks that apply to the account. The review skill and the auditor say seventy where they said seventy-three, and give coverage as the count the result gives, "from N of the M checks that apply", still never as a percentage. The eval mocks' audit results carry v2.44.0's counts, and fails on any count of the checks above seventy, except in the eval tool snapshots (), which stay as they were taken until they are refreshed whole from the server (issue #5). Tenth (#26), an audit finding's fix. From v2.44.0 a finding can carry its fix in : a ready call ( ), its IDs read from the account and an audit's negatives Exact, or a draft ( ) whose says what only the customer can give, such as the searches to block or the campaign to take budget from. The fix-checks and review skills propose a ready call as it stands, with the condition its gives, ask for a draft's in their one question and never fill it in themselves; the review skill's own PHRASE rule is now for the negatives it writes itself, so it never rewrites an audit's Exact call. Release order: every server half is live, AdCopilot v2.44.0 included, so nothing on the server waits for this version. From v2.43.3 AdCopilot hands an audit question to the fix-checks steps on a running account and tells a Claude that called with no marker, or with the bare , to load the installed skill first, without handing it the turn; against an older server the skill would still run, but would name only the review steps. The marker in every file moves to , and so does the the eval fixtures' answers carry. No eval case had been run for this text when it was written.
0.2.13 (2026-10-20, written against AdCopilot v2.43.1, deployed 2026-10-20 17:51 UTC): the release checklist below says its steps must be run before this version is tagged, and 0.2.13 is released by the founder's choice on 2026-10-20 ("do the plugin release") without its real-account steps 1 to 6, as 0.2.10 and 0.2.12 were, and without the eval suite, as 0.2.10 was, because the change is text only. Step 7: both claude plugin validate --strict runs pass, and so do the structure tests in tests/test_structure.py; neither the suite nor the tools-only ablation has been run for this text. One change to what the plugin tells the model (#23), and nothing to the manifest but the version: Free's weighted operations. From v2.43.1 AdCopilot counts a Free workspace's calls in weighted operations: a full audit or an account health score counts 4, an analysis such as budget pacing 2, listing audiences 2, and every other call 1, except the four that never counted (AdCopilot's own get_org_context and save_org_context, get_resource_metadata and list_accessible_customers), which still count 0, while a paid plan and a running trial count every call 1, and a trial held to Free's 10 a day (paused for its card, or waiting for its first charge) counts as Free does. The daily skill keys its cost on get_org_context's org.ops_weighted, which v2.43.1 adds to the org block, because a held trial's plan still reads pro. The daily skill's cost (in its run and in its set-up), this README's, /adcopilot:audit's (6 of Free's 10 for its two calls) and /adcopilot:weekly's (about 5 or 6 for its four reads), each with 1 or 2 more for every related read run, now say so: the morning check's five reads come to 7 operations at that table, and a run to about 8 to 10 of Free's 10. Nothing changes for a paid plan. AdCopilot sends ops_weighted only while its weighting switch is not off (v2.43.1 runs it at free); set back to off, the daily skill says reads again on its own, but this README and the two commands would overstate Free's cost, so this text goes back with the switch. .gitignore now keeps Python's compiled caches out, and tests/test_structure.py holds each sentence and fails on a tracked cache. The marker in every file moves to claude-plugin/0.2.13, and so does the plugin.version the eval fixtures' get_org_context answers carry. AdCopilot reads any version from 0.2.7 on, and v2.43.1 is live, so nothing on the server waits for this version.
0.2.12 (2026-10-17, aligned with AdCopilot v2.41.0 on 2026-10-18): the release checklist below says its steps must be run before this version is tagged, and 0.2.12 was tagged on 2026-10-19 by the founder's choice ("release Plugin 0.2.12") without its real-account steps 1 to 6, as 0.2.10 was. Step 7: both claude plugin validate --strict runs pass, and the suite (Claude Code 2.1.303, judge sonnet, against AdCopilot v2.42.0, 2026-10-19, evals/results/rerun-v10-2026-10-19T14-27-25Z, gitignored) passes every case at 0.8 except the three habit-offer cases. Those split by run-to-run variance: across the v8 and v10 runs the same unchanged case passed and then failed (habit-offer-chat 0.956 then 0.778, habit-offer-untold 1.000 then 0.690), so the founder chose to ship the v8 wording rather than iterate it on two or three runs per case. The tools-only ablation is green: review-follows-related-reads holds as a guard (tools-only 0.932) and meta-connector-absent, review-question-first-roomy and daily-scheduled-run are gated with deltas of +0.333, +0.292 and +0.238. Changes to what the plugin tells the model, and nothing to the manifest but the version. They come from one conversation on a workspace whose plan has no daily limit, on 2026-10-14, and every sentence is written to be true against AdCopilot v2.41.0, which must be live before this version is published: the dates, the coverage count and the list of costly searches below are that release's. First, the read ceiling. cross_reads.ads_reads_max is how many Google Ads reads the server sized the situation for: the size of one answer, not a workspace, plan or daily limit. Every surface that names it now says so, says the day itself is short only when cross_reads.reads_left_today is present, and offers a read left over as the next read, in a statement rather than a second question ("The next read would show how each campaign bids; say the word and I will run it."), rather than announcing it as a limit; the review in that conversation stopped at its five reads and said it had hit "today's read limit". adcopilot-review also gains a rule under What breaks: a daily limit, a quota or today's look-ups is mentioned only when reads_left_today is present or a call returned the daily-cap refusal. /adcopilot:daily and adcopilot-launch stop calling the number a cap. Related reads still count toward the answer's reads. From v2.41.0 AdCopilot sizes an account that spends with nothing counted at eight reads instead of five on a day with no daily limit, or a limit of at least 6,000, outside a scheduled run: the five it names, then up to three related reads. Every other day and situation keeps its number, and a scheduled run, which skips related reads, keeps five. Second, dates and counts. adcopilot-review and the auditor end their own reads on the that and the other lenses carry, and say the dates once. From v2.41.0 AdCopilot ends the audit's window and the other analysis windows on the account's yesterday, the last full day in its own time zone; before, it ended them on UTC's, and in that conversation the audit's seven days ended on 12 Oct while the account's only result came on 13 Oct. This month's pacing () and the change log () run through the account's today, the day in progress, so the review and the auditor give their dates on their own and never end a read on them. They give a finding's shorter window its dates with any later result in the same sentence, and give coverage as the count the result gives, "N of 73 checks gave a result", with reasons for the rest only in the result's own words; dates its two weeks and any other window the same way. Third, the costliest searches. Before giving a count of searches, the review and the auditor look for their names in every result they hold. That means ' "Costliest search terms with no conversion", which v2.41.0 adds (the costliest first, with their clicks and cost, and how many there are), and the wasted-spend check's list in or , which names each search whenever the account counted a result in the window. When one lists them, the top ones are named with their cost. Where the list says nothing was counted anywhere in the account, they are called the costliest searches, not proven waste. When what they hold only counts them, they say so, and they offer another read for them only when a related read's own reason says that read shows them. and name them from the same list. Fourth, answers a question about AdCopilot from the one call. It makes no account read, starts no other skill, and follows none of , the playbook, or on that turn, not even their offer of a skill's steps (a limit names is kept). It says what AdCopilot can do as one list, says what each routine reads and proposes while leaving approval to the one line on what AdCopilot may change, and ends on exactly one question offering one read, tied to one fact from the 30-day summary with the dates names. From v2.41.0 the server itself has a question answered first and the installed steps offered after. Fifth, approval. No sentence in the skills, the commands, the agent, the manifests or this README promises a yes before every change without saying what makes that true, and the condition is now the same for every AI client: AdCopilot proposes each change first, and the hard stop is your AI client set to ask before AdCopilot's write tools, in Claude and, in Cursor, the run mode with the write tools kept off the allowlist (Cursor's cloud agents never ask). The earlier 0.2.12 text gave Claude's setting alone as the condition, and Cursor has no such setting. That covers the plugin's description in the plugin directory, ' description in the command picker, which now promises nothing, and the line the auditor's report carries, which now says each change is a proposal for the customer's own conversation. A related read's own sentence says a change is still only proposed. fails on such a promise without "as long as" or "while" in the same sentence, in the second person on every surface and in the third person in this README, the manifests, every description and the auditor's report, and it fails on the Claude-only form of the condition anywhere. A rule the model is given in a skill's body, to apply a change on the customer's yes and not before, keeps no condition, because the model keeps to it whatever the client's approval setting. and this README also name the one exception v2.41.0 brings, and how far it goes: Claude Code (version 2.1.214 or later) asks before every use of , the tool that pauses a campaign or switches one on, whatever its settings, and a run with nobody there to answer cannot use it; the Claude desktop app may also ask every time. Every skill and command that pauses a campaign or switches one on now does it with that tool, never with a status in , which v2.41.0 also accepts and does not hold. Sixth, 's description names both routes, through Analytics or directly in Google Ads, and says what the direct route needs as the skill walks it: no Analytics, and its conversion tag on the site through Tag Manager. now says to call first, as every other surface does: v2.41.0 can add a note to a first call that skipped it, once that switch is on. Seventh, Cursor. AdCopilot's connector is listed on cursor.directory, and Cursor reads skills from or , so the plugin's text now checks for Cursor first wherever it works out which client it is in: , , the daily-habit block, 's own paragraph on it and 's set-up. In Cursor it never runs (Cursor's agent can reach a shell where Claude Code is installed, and that command lists Claude Code's connectors), looks for the connector in Cursor's MCP settings, where one added from cursor.directory may be named , and gives Cursor's sign-in step in place of the plugin's Connectors tab; 's fix for an out-of-date tool list gives Cursor's step first (turn AdCopilot off and on again in Cursor's MCP settings) in place of . The daily-habit offer now follows AdCopilot's own decision for the client: it is made only when hands over , which AdCopilot sends only when its is set, for Claude and Claude Code and never for Cursor, and it is worded by ; as on the server, a scheduled run holds the offer back once it has checked in on two different days. In Cursor the plugin makes no schedule offer of its own, but when asks for an offer of a check-in or a summary (v2.41.0 still closes a Cursor turn with one while the routine is pending), it makes that offer as says, with the playbook's close for the steps. A no is recorded in only from Claude, because AdCopilot keeps that answer for the whole workspace and a record made in Cursor would stop the offer in Claude too. Every command now knows what to do when the skills are not installed, because cursor.directory installs commands one by one: , and follow 's and playbook, and , , and send with no version there, because on a versioned marker v2.41.0 hands the turn to the skill that owns the account's situation and leaves that skill's steps out of and the playbook, while on the bare one it names no skill and gives the steps itself. cursor.directory's copies of the commands are 0.2.11's until they are pasted there again, so none of this reaches them before then. The auditor agent carries for Cursor, whose subagents have no tool list and inherit every tool; its list of read tools is enforced only in Claude Code, and Cursor does not say holds back an MCP write. This README gains a Cursor section under Install and Google's disclosure, word for word as AdCopilot's site gives it. Eighth, four changes after the eval runs of 2026-10-18. carries : the model can no longer start it on its own, as it did for a question about which searches waste money in place of , and it runs as before when it is typed. neither offers nor mentions a related read it skipped because it does not bear on the question, and the figure in that read's stays out of the answer. The daily-habit offer, the same in all seven files that carry it, is a paragraph of its own, right after the paragraph or list that gives the finding and never added to the end of one of its sentences, and two sentences in fixed words, with no semicolon and no set-up step before a yes: a read-only check that runs by itself every weekday at 8:07 would have caught the finding on its first morning, opening with "All clear" or "Needs a look" plus one yes/no fix; then, if they say "schedule it", the offer to set it up for where they are. At the close, the first sentence says the same check can look over the account each morning. On those runs every judge vote failed the earlier wording on the check that the offer is one or two sentences with no set-up step, and on a later run that check failed again only where the offer was added to the end of the paragraph about the searches. 's opening line names every finding that needs a look. Five new cases are written; three of them ran once, on 2026-10-17, against an earlier 0.2.12 text, and two never have (see the Evals section). Three are owned by : , (a question answered first on a plan with no daily limit, with eight reads) and (one set of dates ending on the account's own yesterday, and the costliest searches named). Two are owned by : and . Every answer in them carries v2.41.0's own overlays, hints, , playbook and for its state, worked out and rendered by that release's code, and the account-day case's wasted-spend and search-terms findings are that release's evaluators' own, with this month's pacing and the change log running to the account's today. The first of the two docs cases is the state of that conversation's server answer, an account spending with nothing counted handed to ; the second is a counted account handed to . Each item of their rubric is its own llm grader, numbered as in the rubric, and the no-review item allows what the workspace answer itself says: the suite's judge answers one word, and given every item at once it failed replies it passed on each item alone. The fourteen older cases with a answer of their own are re-rendered the same way, from their own data, by v2.41.0's code, with two values in that data changed rather than carried over, as each fixture's header says: is in five of them (it was ), so that carries no close where the case says it asks for none, and gains a day before its last sighting, because v2.41.0 believes a schedule only once a scheduled run has checked in on two different days; 's answer is the client's sign-in error, not a server answer, and the suite-wide answer in , which eight cases share, is left as it is. Their call-log graders now look for a call by its own , because v2.41.0's playbook and name tools inside the answer itself, and the account-day case's date graders also read a bound on the day after, a single day, a date in double quotes, a range that takes in the account's today, an open range (a or bound with no upper bound, which runs to the account's today) and an list holding a wrong day. Neither the suite nor the tools-only ablation has been run for this text, so none of those cases has run on its new answer. The marker in every file moves to . AdCopilot reads any version from 0.2.7 on, but this text describes v2.41.0, so that release goes out first.
0.2.11 (2026-10-13) is untagged, and the release checklist below has not been run for it. Two changes to what the plugin tells the model, and nothing to the manifest but the version. First, the daily-habit offer moves from the end of the reply to right after the first finding the morning check also reads (the searches that spent with no sale, a campaign out of budget or off its budget pace, a disapproved ad, a drop against the 7-day average, a change nobody asked for), tied to it, with the value line: a check that runs by itself every weekday at 8:07 would have caught it on its first morning, it only reads the account, and it opens with "All clear" or "Needs a look" plus one yes/no fix. For any other finding, even one that costs money (a tracking fault, a Quality Score problem, a targeting problem), or none, the offer closes the reply, before any question the reply ends on, and does not say the check would have caught it, because the morning check does not read those. The seven files that carry the block and adcopilot-review's own paragraph change together. A no now ends the offer for the conversation, asks once whether the customer's Claude can create scheduled tasks, and records routine.own_schedule as unavailable if it cannot and declined if it can, which AdCopilot holds for a week and then lets the offer come back; get_org_context shows declined only for that week, so the plugin does no date arithmetic. The offer stays a statement ("say "schedule it""), not a question, so a reply never ends on two. Second, the cost of a run: about six or seven reads, not five, each counted toward the workspace's daily operations, and a Monday that also runs the weekly summary can use the whole day's, which on Free is 10. Release order: this version is written against the AdCopilot build that accepts declined; on an older server that call is rejected by name, so the server is released first. The eval graders for the offer no longer look for it "at the end", and the three habit cases have not been re-run for 0.2.11. The marker in every file moves to claude-plugin/0.2.11.
0.2.10 (2026-10-12) was tagged on 2026-10-13 without the release checklist below being run for it; the founder chose to tag it unrun, and its release notes say so. Three changes to what the plugin tells the model, and nothing to the manifest but the version. First, a finding whose own text carries a condition. Every surface that relays a lens's findings to the customer now carries one sentence: a finding that says "if X" is not a step until X has been checked, so either make the read it names and say what you found, or carry the condition into your own sentence, and never turn it into an instruction. Nine surfaces carry it: the auditor agent, /adcopilot:audit, /adcopilot:daily, /adcopilot:negatives, /adcopilot:weekly, and the adcopilot-review, adcopilot-daily, adcopilot-launch and adcopilot-measure skills. adcopilot-seo's own steps name two lenses only to say what they do not cover and run none, so it carries nothing; it can still reach analyze_waste or analyze_search_terms when a raw search it makes ends with related_reads, and carries no sentence for that case, and tests/test_structure.py fails on any file that names a lens and is neither covered nor excused. The names it looks for are the twelve lenses the auditor enumerates, which a test holds the list to include, and explain_conversion_gap, which the auditor is also tooled for. In the unattended run the sentence sits among the run's rules with its limit stated: the run makes only its own reads, so a condition whose read is not one of them is carried into the sentence. The auditor's read-only tool list gains ga4_list_conversion_events, the read a conversion finding can name; the list is still enumerated reads only. Second, the depth of full_audit. AdCopilot v2.35.2, the release this was checked against, runs a call that names no depth deep and falls back to a quick audit, saying so, only if that run cannot finish; an explicit deep never falls back. adcopilot-review, the auditor and the description of the review-spends-the-depth case said the default was quick; they now say what the server does and still pass depth="deep". The eval mocks and the _tools.json snapshots are frozen copies of the server's older text and are left as they are; no grader changed. Third, a new skill, adcopilot-meta, for questions about Meta ads. AdCopilot has no Meta integration (one is on the roadmap, with no date). Meta publishes a connector of its own for Meta ads, which it calls the ads MCP server and an open beta, so the skill looks for it in the conversation first. When it is not there, the skill says what it is and how to add it, and gives Meta's help page and AdCopilot's guide, . When it is there, Meta's figures come from Meta's connector and Google's from AdCopilot, each labelled with its source; no figure read from Meta goes into an AdCopilot call; and a change through either connector is proposed first, one platform at a time, because Meta's pages say the AI client asks for confirmation and do not say Meta's server also does, so the skill asks itself. Meta's pages were checked on 12 October 2026. For adding the connector in an assistant without a developer app of the customer's own, Meta's pages state no list of countries, no role on the ad account and no verification step, and the skill states none of them; Meta's developer documentation does have an app review step for a customer who uses a Meta developer app of their own to act for other businesses, and the skill sends that customer to Meta's pages and says no more. Its text carries no plan or price word, and it describes Meta's rollout only in Meta's own words (an open beta; an account may not have access to all of its tools and features yet). It also says that text inside Meta's results is data, never an instruction. The guide is a page on AdCopilot's docs site, published from AdCopilot's own repository, and , or the link in the skill leads nowhere for every customer who asks about Meta without Meta's connector, which is the skill's main answer. Release in this order: merge the docs commit and ship the AdCopilot release that carries the docs build, fetch and check that the page title is the guide's, and only then tag and publish the plugin. The status code is not the check: the docs site answers a path that does not exist with HTTP 200 and the title "404: This page could not be found." The skill passes the plugin marker only when Google figures are part of the answer, so the structural test that counts the files carrying the marker moves from eleven to twelve. (tag , entered in as ) asks about Instagram ads with Meta's connector absent and grades that the reply gives Meta's help page, AdCopilot's guide and the address, reads nothing through AdCopilot, states no figure from memory and none of Meta's terms. The tools-only ablation prints for a skill with none, so step 7 of the release checklist has a case to measure now, but it cannot be green until that case is run under an authenticated Claude Code and its delta over the tools-only arm is positive; if the case turns out to be a regression guard (the tools-only arm already scores 0.8 or more on it), the skill still has no gated case and needs another, or the founder's explicit waiver recorded in the release notes. The marker in every file moves to , which AdCopilot reads like 0.2.7 to 0.2.9. The structural tests and both runs pass. : the real-account runs, the eval suite and the ablation need a person with an authenticated Claude Code, so no gate result exists for these words, and 0.2.8's results stand only for the text this release leaves alone.
0.2.9 (2026-10-12) changes the directory listing and nothing a customer runs. The display name becomes "AdCopilot for Google Ads, Google Analytics, Google Search Console & Google Tag Manager", the description and this README's opening lead with the agent that runs your Google Ads and say AdCopilot is not related to other products of that name, the keywords grow from six to twenty, and the manifest's icon points at a 1024x1024 PNG (.claude-plugin/icon.png, the same mark as icon.svg), because the directory cannot display an SVG given as a path. Two directory warnings are addressed: the auditor agent's tool list drops Read, which it never used (a report is its return value, and the list stays enumerated reads only), and a .gitignore comment no longer mentions running anything. The marker in every file moves to claude-plugin/0.2.9, which AdCopilot v2.34.10 and later read like 0.2.7 and 0.2.8. No skill, command or agent body changes beyond the marker, so 0.2.8's gate results stand for the text; the suite and the ablation have not been re-run for 0.2.9. It was tagged on 2026-10-13 on that basis.
0.2.8 is written against AdCopilot v2.34.19 (deployed 2026-10-09 21:04 UTC), the release whose lens results end with related_reads and whose get_org_context answer carries question_map; it was published on 2026-10-10 and tagged on 2026-10-12, and its gate is recorded in the 0.2.8 note below. 0.2.7 was released against AdCopilot v2.34.10 (deployed 2026-10-08 08:00 UTC; connector tools revision 7b2eee34, read live from get_org_context on 2026-10-08), the first release that reads the version on the plugin marker. The components were written against AdCopilot v2.34.0 (connector tools revision 7016a0aa, read live on 2026-10-06). The review, docs, Search Console and auditor components were written and checked against that server; the earlier flows were written against v2.30.2 and re-checked against v2.34.0 for the claims they make. The tool snapshot the eval suite mocks against (evals/mocks/adcopilot/_tools.json) is frozen at connector tools revision 0066a60e, taken 2026-09-23 under v2.14.1, except its get_org_context entry, which is the live schema at revision 782ec74d (read 2026-10-03) so that the eval model is offered source as a real client is — every case that reads the server fails its marks-plugin-use grader on a get_org_context call without source: "claude-plugin/<version>", the bare "claude-plugin" included, because the version is what the server routes on (the tools-only ablation's stub carries the same versioned marker, so that grader adds nothing to any skill's delta); the live revision moves independently of this plugin, and a difference between the two is not a fault. /adcopilot:audit relies on the tools_revision that get_org_context reports from v2.14.1 on, to refresh a stale tool description in-conversation. The skills, the four commands that read the account and the auditor call get_org_context with source: "claude-plugin/<version>": the plugin marker followed by this plugin's own version from .claude-plugin/plugin.json. It is written out in every file, because a skill cannot read the manifest at run time, and tests/test_structure.py holds every copy equal to the manifest, so a version bump moves them all or fails. AdCopilot reads the bare "claude-plugin" from v2.29.0 on to record plugin use per workspace. The version after the slash is read from the AdCopilot release that routes on it (v2.34.10, live from 2026-10-08): on a call marked by plugin 0.2.7 or later, the first version whose version it reads, that server hands the turn to the installed skill that owns the situation instead of repeating its steps in its own playbook — , , , or for a running account whose results are counted. The bare marker, a version below 0.2.7 or one it cannot read counts as no version: the call is recorded and answered with the server's own playbook, as before, because no release before 0.2.7 carries the sentence below. That server routes by the account's situation, not by which command or skill made the call, so each of them, when the answer's names a different skill, does the request it was given, keeps any limit adds, and offers that skill's steps afterwards: on an account whose results are not counted still writes the week, and offers the measurement steps after it. A server before that release does not recognise the suffixed value: it records no plugin use, and the reminder to install the plugin comes back for those workspaces, so that release is live before any plugin version carrying the suffix is published. Calls without the marker behave as before. 's unattended run passes instead, and the daily-habit offer stops when reports , a date in , or or , the three things that stop the server's own schedule offer; the set-up records itself (, which AdCopilot accepts even while its emailed check-in holds the routine, or on a Claude with no scheduled tasks), because from 0.2.7 the server leaves its own schedule section to this plugin on a turn a skill owns. AdCopilot records that marker, and reports , (the account its own check-in reads) and , from v2.30.2 on; on an earlier server the value is ignored and those fields are absent, so the offer stops only for a schedule recorded with , which AdCopilot refuses while its own emailed check-in () holds the routine. The instructions the set-up hands over are AdCopilot's recipe (, from v2.30.2 on) word for word, and holds the skill's copy to it. The new cases for and the scheduled run add case-level entries for , , and , read live at revision on 2026-10-04, and their mocks carry the new fields; the suite-wide snapshot is unchanged. The cases for the review and Search Console skills do the same: pins and at revision and at revision , and pins the five Search Console tools at , all read live on 2026-10-06. is pinned deliberately: the suite-wide snapshot's older copy of its description says findings come back “ranked by impact”, and the live one says the opposite — failures first, then by severity, not impact — which is the fact the review case is built on, so a case left on the snapshot would have measured the skill against a description that contradicts the server. Each tool's is a hash of that tool's own text, so tools read in the same listing can report different ones; only the value returns is the connector's.
Evals, and how a skill earns its place
The suite in evals/ runs with claude plugin eval . --trust-plugin --ablation none --threshold 0.8 --judge-model sonnet --concurrency 2 --max-cost-usd 20 --no-publish, which the release checklist below runs before a version tag — by hand, because it drives a model and no workflow here holds a credential to sign Claude Code in (see What CI runs, and what it does not at the end) (the judge model is not incidental: the same case scored 0.81 under the default judge and 0.905 under sonnet). The harness's own with/without comparison removes the whole plugin — connector included — so it cannot say what a skill adds; evals/ablation/tools-only.sh runs the same cases against a copy of the plugin whose skills are cut to their frontmatter and prints each skill's delta over that tools-only arm. A skill whose cases show no delta there is deleted, not kept; the gate reads on each skill's weakest case as well as its mean, so one strong case cannot carry a dead one — and a case the tools-only arm already passes at 0.8 is a regression guard on the connector, not evidence about the skill, so it is held at 0.8 and left out of the delta. Fixtures under evals/**/mocks/ are a fictional tenant: the field names follow the live connector's answers and every value is invented. Each of the nine skills owns at least one case — thirty cases across the suite, which is what evals/ablation/delta.py's TAG_TO_SKILL records — and a skill with no case of its own fails the gate outright, because nothing measures it. 0.2.14's fix-checks-three-checks, for adcopilot-fix-checks, and review-scheduled-unmarked, for adcopilot-review, had not run when this section was written. The six newest cases (meta-connector-absent, for adcopilot-meta; review-uncapped-no-limit-words, review-question-first-roomy and review-account-day-named-searches, for adcopilot-review; docs-capabilities-untracked and docs-capabilities-routed, for adcopilot-docs) have not run on this text, so they have not yet earned their place: four of them (meta-connector-absent, review-uncapped-no-limit-words, docs-capabilities-untracked and docs-capabilities-routed) ran on 2026-10-17 against an earlier 0.2.12 text, where meta-connector-absent scored 0.778, under the 0.8 line on all three of its runs, and the other two have never run. The four cases added in 0.2.5 (docs-safe-to-let-loose, review-spends-the-depth, seo-discovered-is-not-a-verdict and seo-two-numbers-that-lie) are built the other way round from the rest: each one grades only what its skill's BODY adds, after subtracting what the tool descriptions and the server's own already say. Since the Cursor round their answers are v2.41.0's rendering for a call carrying the versioned marker, which the tools-only stub sends too, so the baseline arm is the plugin present with its skill bodies empty: it gets the hand-off to the installed skill, not the full branch a connection without the plugin would get, and a delta can include text the server gives such a connection. For the review skill that meant dropping “biggest money first” as the thing measured — the live playbook for a tracked account says it in those words — and measuring the deep-depth call, the coverage number, the micros rule and the two-sided recommendation instead.
0.2.5's ablation is also the first run where the gate said no, and what it caught is worth keeping: adcopilot-seo's only case scored 0.852 on the tools-only arm, above the 0.8 line, so it was classed a regression guard, left out of the delta, and the skill was left with no gated case — FAIL, “no case the baseline does not carry”. The skill was not the problem. The case was: six of its seven graders passed on the baseline. Three traps written as the skill's knowledge turned out to be things a capable model already does — it passes row_limit explicitly without being told to, it does not reach for a write tool unasked, and it knows an average position is an average. Only the sitemap's dead indexed field survived (1/3 on the baseline, 3/3 with the skill). The remedy the rule prescribes for a skill with no delta is deletion, and it would have been the wrong one here; the right one was a case aimed at what that arm got wrong, which is seo-discovered-is-not-a-verdict. So: a red ablation names a skill, but read which graders moved before believing it is the skill that is dead. Two negative deltas in the same run — audit-stale-tools -0.095 and habit-offer-code -0.167 — are each one llm rubric that both arms fail some of the time, with every deterministic grader passing 3/3 in both; they are variance, not regressions, and the per-grader breakdown is how you tell.
0.2.5's green table, all seven skills ok on their gated cases: review +0.500, docs +0.370, launch +0.295 (min +0.292), daily +0.294, seo +0.250, measure +0.238 (min +0.143), connect +0.189 (min +0.078). It was composed from two runs rather than one, which is worth saying plainly: the 20-case run above, plus a --case 'seo-*' re-run for the case written in answer to it, spliced and re-scored through delta.py. Same judge, same claudeVersion 2.1.289, same day, and no commit touched skills/, agents/, commands/ or the manifest between them, so the twenty older cases were measured against byte-identical content. A single whole-suite run is the cleaner artefact and costs about $35; splicing is honest only while that last condition holds, so check it with git log -- skills/ agents/ commands/ .claude-plugin/ before relying on one.
0.2.6 (2026-10-07) was measured PARTIALLY, and here is exactly how much. Three cases were re-run — seo-discovered-is-not-a-verdict 1.000, seo-two-numbers-that-lie 0.926, audit-stale-tools 0.905 — 3/3 at the 0.8 threshold, overall 0.944, judge sonnet, claudeVersion 2.1.292, $2.87, 236s. Those are the only three cases whose inputs changed: the release touched skills/adcopilot-seo/SKILL.md (body only), commands/audit.md and agents/adcopilot-auditor.md, and nothing else under skills/, commands/ or agents/. The other eighteen cases are carried over from the 2.1.289 run against byte-identical text for their skills. No whole-suite run under 2.1.292 exists. That is a weaker artefact than 0.2.5's and is recorded as such: the carried-over scores are honest about the TEXT and silent about the BUILD, which is the one comparison this file keeps warning about.
The seo ablation was run fresh on both arms under 2.1.292, because a body-only change cannot move the tools-only arm but a build change can move everything: seo-discovered-is-not-a-verdict 1.000 vs 0.750 (+0.250), seo-two-numbers-that-lie 0.926 vs 0.778 (+0.148), so adcopilot-seo is ok on a mean of +0.199 and a minimum of +0.148. Only seo needed it — commands/audit.md is not stripped by tools-only.sh (it cuts skills/*/SKILL.md only, line 51) so it moves both arms equally, audit-stale-tools is tagged audit which is not in TAG_TO_SKILL and so owns no skill's gate, and agents/ is outside the suite entirely (see the eval-coverage issue).
And a reclassification worth more than the scores. seo-two-numbers-that-lie scored 0.852 on the tools-only arm under 2.1.289 — above the line, a regression guard, excluded from the delta, which is what left adcopilot-seo with no gated case and threw 0.2.5's first FAIL. On 2.1.292 the same case text with the same judge scores 0.778 on that arm, so it is now a gated case and seo has two. Nothing in the case or the skill changed between those numbers. So 0.2.5's "the skill was not the problem, the case was" diagnosis was half the story: the classification is a property of the BUILD as much as of the case, and a skill can pass or fail the gate on a client upgrade alone. Re-read the class column before acting on a FAIL, and never carry a Class verdict across builds.
0.2.7 (2026-10-08) is the release the server's routing waits for. The marker says its version — every interactive get_org_context call, fourteen sites in eleven files, passes source: "claude-plugin/<version>" with the manifest's version — and from AdCopilot v2.34.10 the server reads it: on a call from 0.2.7 or later it hands the turn to the installed skill that owns the account's situation instead of repeating that skill's steps in its own playbook. The rest of the release is what makes that hand-over safe. Every yes closes a loop: adcopilot-review applies the fix, reads back only what the result does not show, says how to undo it, records it and proposes the next fix the same way; /adcopilot:audit ends on its first fix as a proposal; a yes to a finished scheduled run re-reads what the fix touches before the change is made; and adcopilot-review carries the two rules the server's counted-account branch used to state (no bidding change in the weeks after something went live, and on a day with few look-ups left, the sized reads first, then the proposal). A routed turn still does the request it was given: one sentence in every marked file, keyed on the answer's plugin.skill. The scheduled run's closing question asks about the change itself, never a change that hangs on a check. And the daily-habit offer also stops on routine.own_schedule saved or unavailable, which the set-up now records, because from 0.2.7 the server leaves its own schedule offer to the plugin on a turn a skill owns.
0.2.7's gate on 2026-10-08 is GREEN on its second run; the first run was red. The second run is the whole suite, once and not spliced, on commit 3bd07bd (the fix round below; nothing under skills/, commands/, agents/ or the manifest has changed since), under claudeVersion 2.1.293, judge sonnet: 21 of 21 cases passed at 0.8, overall 0.942, $18.88, 1,406s. The tools-only ablation is green too, all seven skills ok: review +0.500, docs +0.444, seo +0.347 (min +0.111), daily +0.275, measure +0.274 (min +0.190), launch +0.267 (min +0.167), connect +0.189 (min +0.078). Its with-skills arm is that suite run, passed in as ABLATION_WITH_JSON. Its tools-only arm was run fresh on the same commit, build and judge. The first run's arm could not be reused, because three skill bodies and commands/audit.md had changed since, and the copy keeps commands/ whole, so splicing it in would not be the honest splice described above. ABLATION_MAX_COST_USD=20 let the arm finish all 21 cases in one run, unspliced: $16.98, 793s. No case is red. Five sit within 0.05 of the line, and each is named here so that a drop on the next build is read against it. measure-teaches scored 0.810, with teaches-two-steps failing on two runs of three where the first run had one. setup-duplicate-server scored 0.825: setups-job-and-one-next-step failed on all five runs, against one of five in the first run. The tools-only arm fails that rubric on all five runs in both ablations, and the only change that reaches the case is adcopilot-connect's step 2; this is a lead, not yet diagnosed. setup-signed-out scored 0.829, with sign-in-once-and-stop failing on three runs of five. It is a guard, and its with-skills score sits below its tools-only 1.000 (-0.171), as it did in the first run (0.886 against 0.943). launch-unserved-campaign scored 0.833 and connect-unknown-product 0.843; the latter's failing runs are the address item described in the judge replay below. audit-stale-tools scored 1.000 on both arms, so it is now a guard; /adcopilot:audit is in both arms. 0.2.6 scored 0.714 on it under this build, and that result stands, so the fix ships with this release. seo-two-numbers-that-lie fell from 1.000 to 0.889 with its skill unchanged: one run left the marker off a call and missed one