aga://guides/first-analysis, and the gsc-content-deep-dive prompt walks an
agent through it.
Phase 0 — Site structure (do not skip)
Everything here is cheap to check and expensive to miss.- Call
gsc_get_contextFIRST — before any performance call. Record the publishing base, CMS URL,reverseProxy, matched property set, first publish date, maturity block, and placeholder flag. A reverse-proxy rewrite or migration splits the dataset: every cohort spanning it is invalid. Expect undiscovered migrations here — a proxy change discovered late invalidates every cohort definition built before the discovery. The placeholder flag matters too: accounts still on the shared placeholder space will never match a GSC property, and the performance tools refuse to pull until the real domain is set. - Call
gsc_list_sites— confirm which GSC properties aggregate. - Enumerate EVERY hostname in a page-dimension export (rows carry
tags.hostname/tags.pageClass). Legacy subdomains hide here — it is common to find a forgottenblog.subdomain quietly holding a large share of an intent class at deep positions. - Identify migrations, replatforms, proxy changes. Each is a structural break; cohorts split there.
- Ask the client: what bulk operations ran, and when? What does the content itself record (bylines, modified-time meta, schema.org)?
Phase 1 — Acquisition, on a budget
Google enforces a daily per-property load quota. When it trips, EVERY call fails until it resets the next day — narrower date ranges and tiny row limits fail identically, because it is a property-level daily cap, not a request-size limit. Enumerate the pulls you need before you start.-
Size first. Call
gsc_query_analyticsbefore exporting — it is cheap (totals plus top rows) and tells you whether the range fits. -
Export
[page, date]in monthly slices; verifytruncatedByApi: falseon each. The cap is 25,000 rows per property per call, and a single busy month on a growing site can approach it. Auto-partitioning rescues capped pulls, but then you must verifycoverageRatioof at least 0.98 — avoiding the cap is cleaner. -
Export
[query, date]the same way. -
Pull
[page, query]EARLY for a recent window and readmeta.anonymization— it reports what fraction of impressions and clicks sits on named queries. Do not leave this for later: this block has retracted headline findings after they were already reported. See GSC data fidelity. -
Download every export immediately. The file lives on the MCP server,
not your machine — a local read of the returned
filePathfails. The response carries a pre-signeddownloadUrlthat expires in about an hour:Do not collect URLs and fetch hours later. -
Pull the content corpus with
articles_search(statuses["published"]): slugs,publishDate,updatedAt. -
Pull the content planner per mode, with an explicit
accountId— the session’s current account is an in-memory override that can reset silently, subagents especially. Use the returned rows, notquestionCounts.total, which can undercount badly — the returned rows are authoritative. -
Prefer one wide export plus local re-slicing (
gsc_query_export, or pandas over the downloaded JSON) over many narrow API calls. Identical params reuse the 6-hour cache.
truncatedByApi: false, and every file downloaded
within the hour.
Common first-timer mistake: iterating exploratorily against the API until
the quota trips mid-analysis, then losing the afternoon.
Phase 2 — Panel construction
- Normalize URLs: strip fragments (GSC reports table-of-contents anchors as distinct URLs — thousands of rows may be fragment duplicates that fold back into parent slugs), fold www/non-www, trailing slashes, and query strings.
- Audit every substring path filter. A filter like
page contains "articles"can also match unrelated URLs that happen to contain the string. Enumerate what the filter caught; exclude explicitly and record the impression cost. - Cut the panel at today − 3 days; GSC data settles over about 3 days.
meta.incompleteDaysflags only dates inside that real lag window — a months-old export carries no flags. - Reconcile. Sum the merged exports and compare against a single
gsc_query_analyticscall over the same range. Do not proceed until they match; after normalization, the panel should differ only by your deliberate exclusions. This gate catches filter mistakes, overlapping date ranges, and silent truncation. - Write canonical intermediates — one page panel, one query panel. Everything downstream reads these, so a normalization fix propagates once.
Phase 3 — Ground truth
- Validate
publishDate: does any page show impressions before its recorded publish date? If none do, the field can be trusted. - Test whether
updatedAtis real or a bulk touch: count distinct dates and the max articles sharing one. Hundreds of articles sharing a handful of timestamps means a bulk operation, not update history. - If
updatedAtis dead, look for the true date IN THE CONTENT: the true dates may exist only in the content itself — bylines, schema.org blocks,article:modified_time. Ask the client what their content contains — in practice this field is recovered because the client mentions it, not because analysis finds it. - Compute the instrument’s coverage PER PERIOD (named-query share of impressions) and check it is stable — the named share can double within months as a site grows, and distinct-query counts are not comparable across those months.
- Validate every proxy against ground truth on a sample before it carries an analysis; report the mis-assignment rate. Unvalidated proxies have mis-assigned nearly an entire cohort and silently dropped a large share of a corpus from every denominator.
Phase 4 — Analysis
Split cohorts at every structural break; de-trend every age curve; use balanced panels and report N at every point; run placebos before believing any event study; size segments at page level. The full rule set is the companion guide Analysis guardrails — read it before modelling. It exists because each rule, broken once, produced a confident wrong answer.Phase 5 — Reporting
- Quote bounds, not point estimates, wherever the instrument is biased — page-level ceiling vs named-query floor. The page-level ceiling can be several times the named-query floor; the truth is between, and the bound is the finding.
- State explicitly what each result does NOT establish.
- Keep discarded estimators documented so they stay discarded — both the dead estimator and the one that replaced it belong in the record.
- List the assumptions unverifiable from data, and name who can verify them.
See also
- GSC data fidelity — the instrument watchouts behind every acquisition rule above
- Analysis guardrails — the Phase 4 rule set
- Prompts and resources — invoke this
runbook as the
gsc-content-deep-diveprompt - Search Console deep dive — the lighter-weight export recipe for routine questions