@ads-repo/bidding-update-impact
v0.1.0
Published
Measure whether Google's 17 August 2026 target-based bidding change actually affected a Google Ads account. Corrects for conversion lag, runs difference-in-differences against campaigns the change cannot touch, and reports the spend still at risk. Install
Readme
Bidding Update Impact
Did Google's 17 August 2026 bidding change actually hit this account — and what did it cost?
On 17 August 2026 Google stopped letting budget-limited campaigns on a target-based bid strategy overperform their target. A tCPA of €10 that had been quietly delivering €5 now drifts toward €10. Spend is capped either way; what changes is what that spend buys.
Half the industry is currently answering this question with a before/after spreadsheet. That answer is usually wrong.
Why the obvious approach fails
From a real account (PMax, tROAS 6.0, budget-constrained):
| Window | Spend | Conv. value | ROAS | |---|---|---|---| | 10–16 Aug | 34,577 | 155,915 | 4.51 | | 17–23 Aug | 34,267 | 128,933 | 3.76 |
Reads like a 17% collapse. It isn't. That account's own conversion-lag curve shows only 79.8% of conversion value has matured by day 7 — the post window was still filling in. Corrected, it lands at ~4.7: up, not down.
The naive comparison inverted the conclusion.
On synthetic data with a known answer, the gap is starker still: in a scenario with zero injected effect but a normal seasonal dip, raw ROAS reads 4.99 → 3.74, a 25% apparent collapse. This skill returns NOT_YET_DETECTED and a difference-in-differences of −0.0%.
What it does
- Classifies every campaign into treatment and three control groups, handling the four different fields a bid target can hide in and the campaign types Google excluded. (A live account had an App campaign on Target CPA and flagged budget-constrained — a textbook false positive. App is out of scope.)
- Corrects for conversion lag using the account's own curve, so a fresh window isn't compared against a matured one.
- Difference-in-differences against campaigns the update provably cannot have touched, so seasonality cancels instead of being reported as impact.
- Excludes contaminated campaigns — anything a human re-budgeted or retargeted mid-window gets no verdict, and says who changed it.
- Recommends targets for campaigns still at risk. It never writes to the account.
Output: a self-contained report.html, plus analysis.json for auditing and
raw_pull.json as a permanent snapshot.
Verdicts
| | |
|---|---|
| AFFECTED | Treatment converged toward target; control didn't. Real signal. |
| NOT_YET_DETECTED | No divergence found. The rollout is gradual — not yet, or not at all. Never "unaffected". |
| TOO_EARLY | Post window too short or too immature. No verdict is honest here. |
| CONFOUNDED | Too many mid-window edits to attribute anything. |
Even on a null verdict the report gives the exposure figure — spend sitting in campaigns that were overperforming and are therefore still at risk whenever the rollout reaches them. That number is actionable regardless.
Run it
# demo, no account needed
python scripts/sample_data.py --scenario affected --out raw_pull.json
python scripts/analyze.py --raw raw_pull.json --out analysis.json
python scripts/build_report.py --data analysis.json --out report.htmlAgainst a real account, replace step 1 with the five GAQL pulls in
reference/gaql-queries.md (all validated live) and follow SKILL.md.
Scenarios: affected, null, too-early, confounded — each with known ground
truth, which is how the engine is tested.
⚠️ Time limit
change_event reaches back 30 days only. From roughly 17 September 2026,
change history around the switch date is gone permanently — after that there is no
way to prove whether a target was human-edited or moved by the rollout. Capture
raw_pull.json for any account you might want to analyse before then.
Files
SKILL.md workflow the agent follows
reference/the-change.md what Google actually announced, with sources
reference/methodology.md why the naive test fails, and the honesty rules
reference/gaql-queries.md validated GAQL + the field traps
reference/data-schema.md raw_pull.json contract
scripts/analyze.py classification, lag correction, DiD, verdict
scripts/build_report.py HTML renderer
scripts/sample_data.py ground-truth fixtures for tests and demos
assets/ example reportsRequires Python 3.9+ and the googleAdsServer MCP. No third-party packages.
Read-only throughout.
