Honey-Doers
← Links

Search Strategy — Control Document

Rendered at build time from Search_Strategy.md in the repo — edit the source file and rebuild to update this page.

Search Strategy — Control Document

The single live plan for SEO / AEO / UX on honey-doers.com. This file is state, not history — edited in place. Four sections: who we're up against and how each channel is doing, what to work on right now, what's still a hypothesis rather than a scheduled task, and the numbers that say whether any of it is working.

Current focus, 2026-08-19: I11 Phase 1 is closed, the tracked-query baseline is frozen, and E1 is enrolled at 5 treated / 7 control (Lakeville deliberately excluded). Do not read a verdict before 2026-10-14. Next up while that clock runs: E3, I5t, I11 Phase 3 tail cities. If you're here for an I11 Phase 3 session, go straight to LOCAL_PAGE_REMEDIATION.md — it's self-contained.

File Holds
LOCAL_PAGE_REMEDIATION.md I11 — the active workstream. Self-contained: problem, standard, per-city queue, session protocol
REFRESH_BACKLOG.md I6 — the refresh queue. Self-contained: tiering rules, ranked backlog, session protocol
COMPASS_EXTERIORS_MERGE.md I12 — retiring compassexteriors.com into honey-doers.com. Phase 1 (3 service pages) + Phase 3's inbound redirect reversal shipped 2026-09-04; Phase 2 (blog triage, ~85 posts left) and Phase 3's outbound side next
SERVER_SIDE_CONVERSIONS.md I13 — replacing client-side Google Ads conversion tracking with a server-side upload, mirroring the OpenAI Ads pattern already live in functions/api/contact.ts. Self-contained: problem, phased plan, open decisions. Phases 0–2 shipped; Basic Access approved 2026-09-05; two logging gaps found and fixed same day. A real-gclid test then surfaced the actual blocker — Google has closed the legacy upload endpoint to new integrations, mandatory Data Manager API migration for everyone by 2026-06-15. Phase 2b (new, scoped, not started) covers the rewrite
REVENUE_ATTRIBUTION.md I14 — closing the loop from a visit or phone tap to a sold job in JobTread, then layering in post-lead/post-sale communication: static tracked phone number (vendor vs. Twilio build) + a JobTread API lead push + status/newsletter/review/referral sequences. Self-contained: problem, phased plan, open decisions. Scoping only, not started
Search_Strategy_Rules.md Operating rules, experiment mechanics + contamination controls, Business Questions register (B1–B17), decisions log
SEO_FINDINGS.md Dated audits and data sessions, append-only, never edited
TOPIC_MAP.md Per-post inventory + de-facto GSC query, regenerated per data pull
seo-baseline.json + scripts/seo-baseline.mjs Frozen tracked-query baseline; freeze / compare against a GSC deep pull
scripts/city-page-uniqueness.mjs I11 acceptance check — run against dist/ after build

Two rules worth carrying even if you read nothing else: no ranking or performance claim without a dated source (a row in seo-baseline.json or a section of SEO_FINDINGS.md), and every commit that advances a tracked item names its ID ([I11], [E1], [B6]). Full rule set in Search_Strategy_Rules.md.


Competitors & channel performance

Competitive set

Four competitors audited to date — three live-web audits 2026-08-19, plus two from the earlier SERP baseline (I2a, 2026-07-29). Full detail and methodology in SEO_FINDINGS.md.

Competitor Service overlap Organic showing Named in AEO answers? Notable difference from us
Country Creek Builders (Lakeville) Exact match — kitchen/bath/basement/whole-home, same 8 South Metro cities Best of the four: 2 of 4 page-1 hits Yes, both queries run — the only one of the three named Publishes real pricing tiers on-site and in a blog post built around the exact "cost in [region]" query pattern; that post outranked ours in AI-answer synthesis (2026-08-19)
James Barton Design-Build (Apple Valley) Same 4 services + exteriors/additions 1 of 4 — only their own HQ-city (Rosemount) page No, in any of 3 queries One consolidated page per city (not per city×service) at ~2,500 words; "20+ years" as core claim; no pricing
Your Home Improvement Co. (Bath Planet dealer) Bath only, and narrower (fast tub/shower conversion, not design-build) 0 of 4 No 3-level hierarchy (city → service → product variant); largest review volume (676) but no FAQ, no pricing; 9-state footprint dilutes MN focus
South Metro Remodel / thin templated competitors (I2a, Eagan) Kitchen/basement Outranks our pages on several tracked queries at 950–1,400 words, near-zero city-specific content Not tested Thin but keyword-matched; ranking despite ~half our word count is why depth alone was never the explanation — see I11

What this set says, net: page-1 organic for generic service+city queries is hard to win even for well-established local competitors — JBDB and YHIC combined for 1 of 8 attempts on their own turf. Aggregator directories (Angi, Houzz, BBB) fill most of the remaining top slots regardless of who's competing. The one differentiator that visibly moved an AI-answer result was Country Creek's paired pricing-transparency + AEO-shaped content — see B17 in the backlog.

Channel performance (90-day baseline, 2026-04-25 → 2026-07-23)

Channel Baseline Target by Jan 2027 Read
Non-brand organic clicks 46 250+ Pages are ~10–15 weeks old; not yet evidenced either way (Rule 9)
City-service bucket clicks 12 75+ Same age caveat
Service bucket clicks 17 50+
Blog bucket clicks / CTR 192 / 0.60% — / 1.0%+
GBP: profile views / interactions / calls (6mo) 1,600 / 228 / 27 Discovery is 76% Search-driven, not Maps; search terms are almost entirely brand/junk, <15 impressions each for real service queries
AI Assistant channel (GA4) 3 leads/90d (claude.ai, copilot) Quarterly watch only, per I7
Genuine form leads (estimated) ~55–65 75+ with page attribution Estimate until GTM fix lands 30 consecutive days of lead_form_success

Local pack context: with one Lakeville address, GBP cannot win pack placement in distant cities — reviews only break ties among proximate businesses. Organic city pages are the only winnable placement beyond the Lakeville radius, which is why I11/E1 carry the weight they do.


Now / next / later / someday

Replaces the old initiative-status table. An item moves right as its gate clears; it doesn't get re-argued each time, just re-filed.

Now

Next

Ready to start as soon as the item above it clears, or already queued with no remaining blocker.

Later

Contingent on something above reporting first, or genuinely low-urgency.

Someday

No schedule, no owner yet, revisit if a trigger fires.


Backlog

Hypotheses and open questions that aren't scheduled work yet — either because they need an answer first, or because they're genuinely speculative. Full detail for the B-register in Search_Strategy_Rules.md.


Performance tracking

Verified state — 2026-08-02

Re-verified against the codebase; numbers drift, re-check before citing anything below as current.

Fact Value
Blog posts (incl. scheduled) 232
Posts with no metaDescription 144
Posts never refreshed (no updatedDate) 170
Posts with no coverImage 59
Blog → city×service links (of which basement) 30 (12)
Tracked-query baseline frozen 2026-08-19 — see Performance tracking

I11 remediation progress — as of Phase 1 close, 2026-08-19

Metric Pre-I11 (2026-08-02) Now Source
Pages meeting the 30% standard 1 of 104 (Lakeville bathroom, 31.7%) 60 of 104 (13 arm cities + 2 early tail cities) city-page-uniqueness.mjs
Pages with "Photos Coming Soon" placeholder 104 of 104 0 of 104 LocationServicePageLayout.astro
Cities with neighborhoods content 5 of 26 15 of 26 locations.ts
Cities with cityFaqOverrides 1 of 26 (Lakeville, kitchen+bath only) 15 of 26 locationServices.ts
Cities with costBands 0 of 26 0 of 26 — blocked on B1/B6 export locations.ts
Testimonials attributed to a city 0 of 23 9 of 23 src/data/testimonials.ts

Tracked-query baseline — frozen 2026-08-19, corrected same day

node scripts/seo-baseline.mjs freeze, GSC range 2026-05-19 → 2026-08-16. 29 tracked queries, 24 with GSC data. Re-frozen once, same day, same GSC pull — see the measurement bug below — so this reflects the corrected numbers, not the original run.

Finding Value
Total impressions across all 29 tracked queries 3,480
Total clicks across all 29 tracked queries 0
Queries where the tracked page has zero GSC presence 0 of 24 found — every tracked page has some data for its query
Queries cannibalized (tracked page ranks, but a competing page on our own site has more impressions) 5 of 24 — Apple Valley basement, one Lakeville query, Edina, and both Minneapolis queries; the position reported is still the tracked page's own, per the fix below
E1-treated avg position, final arm (5 cities, 7 queries found) 11.6
E1-control avg position, final arm (7 cities, 7 queries found) 10.6

A real measurement bug was found and fixed the same day this froze, before the window meant anything. The original measure() sorted each query's GSC rows by impressions and reported whichever page won as "the" position — so for basement remodeling edina, it reported /blog/'s position (61.4, 7 impressions) instead of the actual tracked page's position, which GSC separately reported at 11 (3 impressions). This wasn't isolated to Edina: the same logic corrupted Apple Valley's number too (city hub's 14.1 got reported instead of the real page's 17.6). The fix reports the tracked page's own row when it has any GSC data at all, and surfaces competing pages as a "cannibalized by" note rather than letting them override the actual metric. Net effect: the arms went from looking badly mismatched (11.1 treated vs. 17.8 control, driven almost entirely by Edina's phantom 61.4) to genuinely well matched (11.6 vs. 10.6) — the original mismatch was a scoring artifact, not a real property of the cities. Full detail in scripts/seo-baseline.mjs's measure() comment.

The zero-click finding is the headline result of this freeze, not a rounding artifact — verified against the raw GSC pull, which shows 159 total clicks site-wide in the window, entirely on brand-name variants ("honey doers," "honeydoers," etc.). Every one of the 29 non-brand tracked queries — the exact query set this whole program is trying to move — converted zero clicks from 3,480 impressions. This sharpens the existing thesis (Honey-Doers converts people who already know the name) into something starker: right now the non-brand city-page channel isn't just underperforming, it is producing no measured clicks at all on its own tracked terms. Per Operating Rule 9, these pages are still ~13 weeks old and E1's actual link treatment hasn't shipped — so this is the honest starting line, not a verdict on the lever.

The wrong-page findings are a free, non-experimental fix. Six tracked queries resolve to the wrong URL — most visibly Apple Valley's basement query landing on the city hub instead of the service page, and both Lakeville queries and both Minneapolis queries landing on / or a service hub. Fixing internal linking/canonicalization so these resolve to the intended page doesn't touch any experiment arm (it's correcting an error, not adding a treatment) and could recover impressions that are currently misattributed. Worth a session on its own.

Deltas over time

The thesis this all tests: Honey-Doers converts people who already know the name; the growth surface is the ~25K non-brand impressions producing 46 clicks sitewide (this freeze shows none of them land on the 29 tracked city-page queries specifically), concentrated in basement remodeling by city, where the right pages already rank page 2 in priority cities. Reviews can't buy local-pack placement outside the Lakeville radius, so organic city pages are the only winnable placement — and I11 existed because those pages looked deep by word count but were ~95% boilerplate before remediation, which is a plausible reason page 2 hasn't moved yet. The frozen baseline above is what E1's actual link treatment will be measured against.


Recurring cadence

Publish cadence: 3×/week (Tue/Thu + one exterior slot), changed 2026-09-04 from the standing Tue/Thu 2×/week set 2026-08-17. The third slot is dedicated to working I12's Compass Exteriors blog backlog (~85 posts categorized in COMPASS_REDIRECT_MAP.md) now that Phase 1 gives that content somewhere real to land — not a general capacity increase. Four picks shipped 2026-09-06 (roof flashing, window sill, deck skirting, roof ventilation); repeatable pick process and both conditions for reverting to 2×/week (backlog empties out, or the quality filter stops finding a real gap — whichever comes first) are in COMPASS_REDIRECT_MAP.md § Picking the next exterior-slot post — don't re-derive either the process or the sunset call from scratch here.

Rhythm Actions
Per session (while I11 is active) Top unticked city from the I11 work queue; close with node scripts/city-page-uniqueness.mjs --cities <city>
Weekly GSC anomaly glance; confirm 30-day publish runway; check I11 progress against freeze date
Monthly gh workflow run fetch-analytics-data.yml; seo-baseline.mjs compare once frozen; regenerate TOPIC_MAP; update this file's Now/Next/Later/Someday buckets
Monthly Ahrefs Site Audit → Link Explorer broken-links export, remediated against the live site. Most flagged rows are Ahrefs' bot getting blocked (403/406/429/401 from Houzz, JLC, EPA, HGTV, ScienceDirect, etc.) — verify each domain with a UA-spoofed curl (and a real browser tab for stubborn ones) before touching anything; only chase rows that are still broken for a real user. Genuine hits are usually one of: an old category-path redirect that dead-ends because a post slug changed since migration, a stray internal link, or a dead outbound citation needing a live replacement (reword the citation if the original claim no longer traces to a live source). See 2026-08-22 session for the full method and the redirect/citation fixes it produced.
Quarterly Append a dated block to Performance tracking; append the next row to the organic conversion rate series; review Backlog B-questions, record any answers with their date
Trigger-based Refreshes fire on decay triggers, not annually — see the post-refresh-audit skill

Full operating rules, experiment pre-registration mechanics, contamination controls, and the append-only decisions log: Search_Strategy_Rules.md.