SEO Title Rewrite
A title rewrite is the cheapest high-leverage SEO action that exists — and the easiest to do on vibes. This skill forbids vibes: the new title comes from the queries the page already surfaces for, and no rewrite ships without a recorded baseline.
The motivating pattern: two pages with the same intent can sit at 1.6% and 11.9% CTR — a 7× spread — purely on how the title meets the dominant query. The winning title is usually already visible in the query data.
Render through growth-report-format. The baseline record and verdict below
are records, not reports, so they keep their own structure — but they still owe
the provenance grammar, the Method block, and the five rules.
Tools this skill calls
| Need | Tool | Key arguments |
|---|---|---|
| The page's real query mix | mcp__google_search_mcp__gsc_search_analytics |
site_url, dimensions: ["query"], dimension_filter_groups (see below), 28-day window |
| Page-level impressions/CTR/position | mcp__google_search_mcp__gsc_search_analytics |
dimensions: ["page"] |
| What the page currently ships | mcp__google_search_mcp__audit_page |
urls: [<page>], response_format: "json" |
Default property: GSC sc-domain:agentman.ai, GA4 property 471838641. Any
other site: resolve the target first.
Scoping the query pull to ONE page
This is the step that silently goes wrong. A query-dimension pull without a filter returns property-wide rows, which look entirely plausible as page data:
dimensions: ["query"],
dimension_filter_groups: [{"filters":[
{"dimension":"page","operator":"equals","expression":"<the full page URL>"}
]}]
Verify it applied. The response carries responseAggregationType:
byPage— the filter worked, these rows belong to your pagebyProperty— the filter did NOT apply; these are site-wide numbers
If you asked for one page and see byProperty, stop. Do not bucket those queries
or draw a diagnosis from them. That exact failure produced a "diagnosis" built
from twelve other pages' queries before it was caught.
Some clients serialise the filter to a JSON string; the tool accepts both forms.
Provenance rules
- Every number names its call. "41276 impressions (gsc_search_analytics, page dimension, 90d)".
audit_pagereturns no CTR, impressions, or position. Those come only fromgsc_search_analytics. Never attach a performance figure to a structural finding unless you fetched it in this run.- A failed or empty call is reported as such. Never substitute an estimate.
- Never cite a prior report or baseline that is not in the current context. If one is needed and absent, read it from the records skill (below) or say it is missing.
- Report the coverage of your query rows. GSC withholds the low-volume tail, so returned rows are often a small fraction of the page's impressions. State that fraction; a bucket analysis over 4% of impressions is a claim about the head, not the page.
Before you start: is a title rewrite even the right fix?
Low CTR does not imply a bad title. Check this before drafting anything, because the wrong diagnosis wastes the rewrite and the baseline.
Pull the page's queries (scoped, per above). Then ask which pattern fits:
| Pattern | Signal | Right action |
|---|---|---|
| Title/query mismatch | Page ranks well on specific queries whose phrasing the title does not use | Rewrite — this skill |
| Intent mismatch | Huge impressions on queries the page cannot satisfy; CTR low even at good position | Stop. The page is shown to the wrong searchers. A rewrite cannot fix this |
| Position | CTR is roughly position-expected | Nothing to fix here; the issue is ranking, not the title |
| SERP composition | CTR far below expected across every query, no obvious title problem | May be an AI Overview or feature taking the click. This suite cannot see SERPs — check by hand before concluding |
Bucket the queries by intent before deciding. A page's headline CTR is an average over intents that may behave completely differently. Sort its queries into groups — instructional, navigational, reference, bare-name — and compute CTR per group. If the intent the page was written for already converts well and the drag comes from intents it cannot serve, that is intent mismatch, however bad the page-level number looks.
Worked counter-example from this property: a post sat at 31501 impressions,
position 6.6, 0.51% CTR — the worst CTR of any top page, with a 114-character
title, which invites the conclusion that truncation was costing clicks. Bucketing
the queries showed its instructional audience converting at 4.92%, while
navigational queries hunting other repositories' skill.md files supplied most
of the impressions at ~1.3%. No title wins those clicks. The rewrite was refused
and recorded as a decision; the real opportunity was a second page for the
reference intent.
Separately: across 90 days on this property, title length and CTR showed no relationship — the best page (5.13%) and the worst (0.069%) sat at opposite ends of the length range. Fix over-length titles because truncation wastes SERP real estate, not because it will move CTR.
Target resolution
- If the request names a page URL (or hands over an opportunity-matrix row), use it.
- Otherwise ask which page. Never guess from a partial slug.
Procedure
1. Pull the page's query reality
gsc_search_analytics for the page, dimension query, page-filtered, last 28
days. Confirm byPage. Sort by impressions.
Identify:
- Dominant queries — the top phrases actually driving impressions (often not what the page thinks it targets).
- The CTR villains — high-impression queries where CTR is far below position-expected.
- Intent buckets — per the table above.
- Siblings — 1–3 pages on the same property with similar intent but strong CTR. Their title patterns are the local ground truth for what this audience clicks.
Then run the diagnosis. If the pattern is intent mismatch, stop, and write a decision record (below) rather than a rewrite.
2. Fetch what's live
audit_page on the URL returns title, titleLength, metaDescription, and
canonical directly. For H1 and og:title — which audit_page does not return —
fetch the page. Note mismatches: a title promising X over an H1 delivering Y is
itself a CTR suppressor.
3. Draft 3–5 candidates
- ≤ 60 characters (pixel truncation is the real limit; 60 chars is the safe proxy).
- Lead with the dominant query's actual phrasing, not the internal product name.
- One idea per title. No pipe-chained keyword lists — that's the losing pattern the data keeps punishing.
- The meta description (≤ 155 chars) answers "what do I get if I click", with a verb.
- H1 changes only if the title/H1 mismatch was flagged; otherwise leave it.
- On an owned brand property, consult the brand-voice skill before finalizing.
Present candidates with the reasoning: which query each one meets, and which sibling pattern it borrows.
4. Record BEFORE shipping (non-negotiable)
Records live in a private records skill, not on disk. In claude.ai and Cowork
the filesystem is session-scoped, so a baseline written to data/ or /tmp/
is gone long before the 28-day re-measure it exists for.
Create the store if it does not exist, then write to it:
- Look for a records skill —
growth-records-<property>(for this property,growth-records-agentman). Usefind_skillsoropen_skill. - If it does not exist, create it with
create_skill: name itgrowth-records-<property>, keep it private (tenant scope), and state in its SKILL.md that it is a durable record store holding real property IDs and is never published. Thenpublish_skillso its files can be read back. - Write the record with
save_skill_resource:- path
records/rewrite-<page-slug>-<YYYY-MM-DD>.mdfor a rewrite - path
records/decision-<page-slug>-<YYYY-MM-DD>.mdfor a refusal
- path
- Read the store's
references/record-format.mdfor the exact field list before writing your first record in a session.
Read back with read_skill_file using the combined path form —
<skill-slug>/records/<file>.md, not a slug argument plus a relative path.
A rewrite record carries:
Type: rewrite
Status: OPEN
Property: GSC <property> · GA4 <property-id>
Page: <url>
Created: <YYYY-MM-DD>
Re-measure: <created + 28 days>
Baseline window: <28-day range> · Fetched: <timestamp>
Source call: gsc_search_analytics(<the actual args, including the filter>)
Aggregation: byPage (verified)
Impressions: <n> · Clicks: <n> · CTR: <x.xx>% · Position: <p.p>
Top queries: <q1 (impr, ctr)>, <q2>, <q3>
Old title: <title> (<n> chars)
New title: <chosen candidate> (<n> chars)
Diagnosis: <title mismatch | intent mismatch | position | serp composition>
Mechanism: <why this title should move CTR for those queries>
Shipped: <date, filled in on deploy>
This record IS the experiment record. For title work it is sufficient on its
own — do not also open growth-experiment, which exists for changes bigger than
a title or for tracking several at once.
A refusal is also a record. When the diagnosis says stop, write a decision
record: what was considered, what the data showed, why acting would not have
helped, and a concrete Revisit if condition. This is what stops the same page
resurfacing as an "opportunity" every quarter.
5. Re-measure (the half everyone skips)
At the re-measure date: same query set, same window length, same tool call, same filter. Compare CTR at comparable position — if position moved more than ~1.5, the CTR comparison is confounded and the verdict is CONFOUNDED, not WIN or LOSS. Google may also rewrite displayed titles; spot-check the live SERP before crediting or blaming the new title.
Append the verdict to the record's own file under a dated heading and update its
Status. Never edit the original baseline block — a baseline whose history is
editable is not evidence.
On a low-traffic page, say so instead of claiming a verdict. If the page earns tens of clicks a month, a real improvement is a handful of clicks and invisible against noise. Report the observed difference with its provenance and state that the page is underpowered for a verdict.
Honesty rules
- Never promise ranking gains from a title change; the claim is CTR at position, nothing more.
- Never promise CTR gains from shortening a long title. Truncation is a presentation defect; the CTR link is unproven and this property's data does not support it.
- A title change CAN move rankings (either direction). That's why the baseline records position.
- Two weeks is the minimum before reading anything; 28 days before a verdict.
- If the baseline doesn't reproduce when re-pulled (it happens — recorded baselines in this suite's own history have failed to reproduce), trust the fresh pull and append a correction noting both figures.
Publishing
Real records name property IDs, page URLs, and event taxonomies. They live only in the private records skill. Never publish a records skill, promote it to system scope, or copy its contents into this master skill — this file must stay portable across properties.