GSC Review: boring-math.com
Repo: C:/Users/skf_s/boring-maths/ (Astro 5 static site, Cloudflare Pages auto-deploys master).
Running checkpoint log: memory file project_boring_maths_gsc_workflow.md. READ IT FIRST. It holds every prior checkpoint's numbers and rulings. Compare against the newest checkpoint; append this run's checkpoint when done.
Mode
- Invoked plain (
/gsc-review): pull, analyze, report ranked levers, then WAIT for "go" before editing anything. Merging deploys production.
- Invoked with "ship" / "go" / "and fix" in the same message: run the full loop including the PR batch without re-asking.
1. Pull fresh data
cd /c/Users/skf_s/boring-maths && npm run seo:gsc-pull
- OAuth token (
scripts/seo/.gsc-token.json) expires ~7 days. On auth failure: npm run seo:gsc-pull -- --reauth + browser consent (sign in as the Google account that owns the property, sc-domain:boring-math.com; consent is clickable via Chrome automation when already signed in).
- The pull prints the REAL totals ("Totals: N clicks / N impressions across N pages"). Use these, not the report summary.
- Step complete when the printed Totals line is captured for the step-3 comparison.
2. Analyze
python scripts/gsc_analysis.py # writes docs/gsc-analysis-<date>.md
python scripts/gsc_region_analysis.py # writes docs/gsc-region-analysis-<date>.md
Data gotchas (these bite every time):
- The report's Summary covers only visible queries (~42% of impressions are anonymized). Page-level totals from the pull output are the truth.
- Query-level clicks are mostly anonymized to 0. Use query positions/impressions as signal; use page-level rows for click truth.
- Page positions are impression-weighted averages; always check query-level before optimizing a page.
- Country splits suppress clicks (country totals reliable, splits are impression/position signal only).
- Read exports with
encoding='utf-8' in Python (cp1252 default chokes).
- Top pages by clicks, direct from export:
gsc-export/<date>/gsc-pages-28d.json, sort by clicks desc.
3. Compare vs checkpoint
Against the newest checkpoint in the memory file: totals, top click pages, the tracked clusters (UK money, activation set, machining, event/party, cooking), and any pages the last run touched. Note: consecutive weekly 28d windows overlap ~21/28 days, so deltas are damped; call trends directional, not proven.
4. Measured laws
- Instant-answer queries: never invest. discount-calculator (~25% of ALL site impressions), percentage-calculator, "X% off $Y": Google answers inline, 0 clicks at pos 9-15. Proven repeatedly.
- Title-tuning striking-distance pages (pos 8-20) does not add clicks without links (raise-calculator, proven Aug-8). Title changes are only for adding missing RELEVANCE terms to deep pages (pos 25-60), with modest expectations.
- UK money cluster + activation set + SMP are authority-bound (parked pos 40-85, 0 clicks). Only backlinks move them. Every report must restate the pitch-table status (
REVENUE.md + docs/outreach/2026-07-pitches.md), empty since mid-Jul 2026, Phase 4 open since Feb.
- Before proposing a build: grep the registry (
src/lib/calculators.ts). Coverage is essentially complete; every "gap" so far already had a page (phantom-build lesson, twice).
- Before proposing internal links: grep actual inbound sources (
grep -rl "<slug>" src/pages --include="*.astro"). contractor-vs-employee looked link-starved but had 11 inbounds; its stall is authority.
- Cannibalization: fix at the root. Usually one page's title claims an intent its content doesn't own (e.g. generic mortgage page claiming "PMI & Taxes"). Differentiate titles to match actual content; cross-link the pair.
5. Lever hunt, in priority order
- New winner pages (clicks appeared without work), reinforce with editorial links if genuinely under-linked (law 5).
- Cannibalized queries with an intent mismatch at the root (law 6).
- Orphans WITH demand: cross-check
scripts/seo/known-orphans.json against page impressions; de-orphan via relatedCalculators entries on topically-close pages.
- Relevance title adds for deep pages (law 2), missing high-impression query words (report section "Content Gaps").
- Worked-example FAQs for long question/word-problem queries already ranking pos ≤12 (homework niche: coffee-spend, event-seating class). Verify every number in the answer by hand.
- Genuinely new demand: only after law 4's registry check, and only if it passes CLAUDE.md rule 5 (real problem, not SEO bait).
6. Execute the batch (only in ship mode or after "go")
git checkout -b seo/gsc-<date>-batch (check git branch first).
- Make the edits. For every new editorial link: add the edge to
scripts/seo/expected-link-edges.json AND remove de-orphaned pages from scripts/seo/known-orphans.json (shrink-only ratchet).
- Gates, all must pass before commit:
npm run build # includes postbuild: trailing-slash, link edges, orphan guard
npx vitest run # ~1106 tests
- Commit: message via Write tool to a file +
git commit -F <file> (PowerShell pipes prepend a BOM). No em dashes in the message. Grep the file to confirm. Stage specific files, never git add -A. Commit the new docs/gsc-*.md reports too.
git push -u origin <branch>, gh pr create, gh pr merge <N> --squash --delete-branch.
- Live-verify: poll
https://boring-math.com/<changed page> for a changed string every 20s (CF Pages deploy ≈ 200s), then spot-check one new link + one new FAQ in the served HTML.
- Writeback: append an EXECUTED/CHECKPOINT stanza to memory file
project_boring_maths_gsc_workflow.md (numbers, PR, what shipped, what was dropped and why, still-gated items) and hippo remember any new lesson.
7. Report format
Lead with totals vs last checkpoint. Then: winners, cluster movement, ranked levers (with the do-not-invest list applied), what shipped (if ship mode) with gate results and live-verify proof, and ALWAYS the Keith-gated items: the backlink pitch sends first, until the table in REVENUE.md has entries.
1---2name: gsc-review3description: Weekly GSC traffic review and improvement batch for boring-math.com.4---56# GSC Review: boring-math.com78Repo: `C:/Users/skf_s/boring-maths/` (Astro 5 static site, Cloudflare Pages auto-deploys master).9Running checkpoint log: memory file `project_boring_maths_gsc_workflow.md`. READ IT FIRST. It holds every prior checkpoint's numbers and rulings. Compare against the newest checkpoint; append this run's checkpoint when done.1011## Mode1213- Invoked plain (`/gsc-review`): pull, analyze, report ranked levers, then WAIT for "go" before editing anything. Merging deploys production.14- Invoked with "ship" / "go" / "and fix" in the same message: run the full loop including the PR batch without re-asking.1516## 1. Pull fresh data1718```bash19cd /c/Users/skf_s/boring-maths && npm run seo:gsc-pull20```2122- OAuth token (`scripts/seo/.gsc-token.json`) expires ~7 days. On auth failure: `npm run seo:gsc-pull -- --reauth` + browser consent (sign in as the Google account that owns the property, `sc-domain:boring-math.com`; consent is clickable via Chrome automation when already signed in).23- The pull prints the REAL totals ("Totals: N clicks / N impressions across N pages"). Use these, not the report summary.24- Step complete when the printed Totals line is captured for the step-3 comparison.2526## 2. Analyze2728```bash29python scripts/gsc_analysis.py # writes docs/gsc-analysis-<date>.md30python scripts/gsc_region_analysis.py # writes docs/gsc-region-analysis-<date>.md31```3233Data gotchas (these bite every time):34- The report's Summary covers only visible queries (~42% of impressions are anonymized). Page-level totals from the pull output are the truth.35- Query-level clicks are mostly anonymized to 0. Use query positions/impressions as signal; use page-level rows for click truth.36- Page positions are impression-weighted averages; always check query-level before optimizing a page.37- Country splits suppress clicks (country totals reliable, splits are impression/position signal only).38- Read exports with `encoding='utf-8'` in Python (cp1252 default chokes).39- Top pages by clicks, direct from export:40 `gsc-export/<date>/gsc-pages-28d.json`, sort by clicks desc.4142## 3. Compare vs checkpoint4344Against the newest checkpoint in the memory file: totals, top click pages, the tracked clusters (UK money, activation set, machining, event/party, cooking), and any pages the last run touched. Note: consecutive weekly 28d windows overlap ~21/28 days, so deltas are damped; call trends directional, not proven.4546## 4. Measured laws47481. **Instant-answer queries: never invest.** discount-calculator (~25% of ALL site impressions), percentage-calculator, "X% off $Y": Google answers inline, 0 clicks at pos 9-15. Proven repeatedly.492. **Title-tuning striking-distance pages (pos 8-20) does not add clicks without links** (raise-calculator, proven Aug-8). Title changes are only for adding missing RELEVANCE terms to deep pages (pos 25-60), with modest expectations.503. **UK money cluster + activation set + SMP are authority-bound** (parked pos 40-85, 0 clicks). Only backlinks move them. Every report must restate the pitch-table status (`REVENUE.md` + `docs/outreach/2026-07-pitches.md`), empty since mid-Jul 2026, Phase 4 open since Feb.514. **Before proposing a build: grep the registry** (`src/lib/calculators.ts`). Coverage is essentially complete; every "gap" so far already had a page (phantom-build lesson, twice).525. **Before proposing internal links: grep actual inbound sources** (`grep -rl "<slug>" src/pages --include="*.astro"`). contractor-vs-employee looked link-starved but had 11 inbounds; its stall is authority.536. **Cannibalization: fix at the root.** Usually one page's title claims an intent its content doesn't own (e.g. generic mortgage page claiming "PMI & Taxes"). Differentiate titles to match actual content; cross-link the pair.5455## 5. Lever hunt, in priority order56571. New winner pages (clicks appeared without work), reinforce with editorial links if genuinely under-linked (law 5).582. Cannibalized queries with an intent mismatch at the root (law 6).593. Orphans WITH demand: cross-check `scripts/seo/known-orphans.json` against page impressions; de-orphan via `relatedCalculators` entries on topically-close pages.604. Relevance title adds for deep pages (law 2), missing high-impression query words (report section "Content Gaps").615. Worked-example FAQs for long question/word-problem queries already ranking pos ≤12 (homework niche: coffee-spend, event-seating class). Verify every number in the answer by hand.626. Genuinely new demand: only after law 4's registry check, and only if it passes CLAUDE.md rule 5 (real problem, not SEO bait).6364## 6. Execute the batch (only in ship mode or after "go")65661. `git checkout -b seo/gsc-<date>-batch` (check `git branch` first).672. Make the edits. For every new editorial link: add the edge to `scripts/seo/expected-link-edges.json` AND remove de-orphaned pages from `scripts/seo/known-orphans.json` (shrink-only ratchet).683. Gates, all must pass before commit:69 ```bash70 npm run build # includes postbuild: trailing-slash, link edges, orphan guard71 npx vitest run # ~1106 tests72 ```734. Commit: message via Write tool to a file + `git commit -F <file>` (PowerShell pipes prepend a BOM). No em dashes in the message. Grep the file to confirm. Stage specific files, never `git add -A`. Commit the new `docs/gsc-*.md` reports too.745. `git push -u origin <branch>`, `gh pr create`, `gh pr merge <N> --squash --delete-branch`.756. Live-verify: poll `https://boring-math.com/<changed page>` for a changed string every 20s (CF Pages deploy ≈ 200s), then spot-check one new link + one new FAQ in the served HTML.767. Writeback: append an EXECUTED/CHECKPOINT stanza to memory file `project_boring_maths_gsc_workflow.md` (numbers, PR, what shipped, what was dropped and why, still-gated items) and `hippo remember` any new lesson.7778## 7. Report format7980Lead with totals vs last checkpoint. Then: winners, cluster movement, ranked levers (with the do-not-invest list applied), what shipped (if ship mode) with gate results and live-verify proof, and ALWAYS the Keith-gated items: the backlink pitch sends first, until the table in REVENUE.md has entries.