Skip to content

SEO content & IA: Twin Cities positioning, service pages, about/contact, redirect pattern - #54

Open
dralgorhythm wants to merge 8 commits into
mainfrom
claude/seo-twin-cities-content
Open

SEO content & IA: Twin Cities positioning, service pages, about/contact, redirect pattern#54
dralgorhythm wants to merge 8 commits into
mainfrom
claude/seo-twin-cities-content

Conversation

@dralgorhythm

Copy link
Copy Markdown
Owner

Summary

PR 2 of 2 of the Twin Cities SEO update (plan: artifacts/plan_seo_optimization.md), branched from current main (includes merged #53). All positioning/copy changes land here so titles and content arrive together.

Information architecture

  • Career coaching moves to /services/career-coaching (searched slug; "guidance" is zero-volume) with a new reusable static-redirect pattern for GitHub Pages: lib/redirects.ts registry + a stub at /career-guidance emitting an instant meta refresh with canonical → destination. Stubs are never sitemapped or internally linked; the metadata-policy test enforces stub correctness.
  • /services hub + three new service pages, each owning one primary query with 600–900 words of distinct first-person copy, a deliberately different local angle, six real-search FAQs (FAQPage schema), Service schema, breadcrumbs, own OG card, and a related-services triangle:
    • /services/software-consultingsoftware consultant minneapolis
    • /services/ai-consultingai consulting minneapolis (cites the agent-coordination essay as proof of practice)
    • /services/engineering-practice-improvementengineering process improvement consultant
  • Homepage rewritten as the metro category page: keyword-first title Twin Cities Software Consulting, founder hero → /about, discovery-call CTA, four service cards (shared config with the hub), AI-native differentiator section, exactly one local trust line.
  • /about (E-E-A-T anchor — Jordan Winters, Minneapolis, beliefs, solo model; Person schema url repoints here) and /contact (free 30-min discovery call framing + NAP line).
  • Nav becomes home/services/coaching/blog/about/contact; footer gains Services/Site/Connect columns + one plain NAP line — previously the footer had zero internal links, orphaning the site graph.
  • Blog: post title tag retargets Coordinating AI Coding Agents (h1 unchanged), byline → /about, intro link repointed from LinkedIn to the practice-improvement service page, closing pointer to /services/ai-consulting; index description drops the fake "team" voice.
  • Discovery surfaces: public/llms.txt + hand-rolled static /rss.xml (linked in head + blog index) — pairs with SEO technical foundation: canonicals, local-entity schema, sitemap/robots, per-route OG images #53's decision to welcome AI crawlers.

⚠️ Merge gate on the email commit

feat(seo): publish jordan@bidwell.info contact email publishes the address in the footer, contact page, home, career-coaching, and org schema. Before merging, create the Namecheap Email Forwarding alias (jordan → your inbox; ~5 min, MX/SPF already live — Namecheap dashboard → bidwell.info → Email Forwarding) and confirm a test email arrives. To merge sooner, drop that one commit — everything else stands alone with LinkedIn-primary CTAs.

Also includes a root-cause fix for a recurring annoyance: the image optimizer rewrites its hashes JSON in a style Biome rejects, so every local build broke the next lint run (the auto-fix bot had already patched the symptom once on #53). The generated file is now excluded from the style gate.

Guardrails honored

No suburb doorway pages, no fake reviews/aggregateRating, no "our team" plurality, geo modifier once per page, distinct local paragraphs (not templated swaps), varied internal anchors.

Test plan

  • npm run qa:full green (type-check, Biome, coverage, production build)
  • npm run test:e2e 13/13 — including the redirect-stub browser flow, rss/llms resolution, and the SEO invariants suite
  • Unit suite 308/308 (76 new assertions since SEO technical foundation: canonicals, local-entity schema, sitemap/robots, per-route OG images #53)
  • Metadata-policy test enforces canonical === route, uniqueness, OG-image colocation, and redirect-stub correctness across all 24 page modules
  • Re-verified after rebasing onto current main (dependabot lockfile bumps): type-check, lint, 308/308
  • Post-merge: health-check workflow green (now covers all new routes via the sitemap); spot-check /career-guidance redirecting in a browser

🤖 Generated with Claude Code

dralgorhythm and others added 7 commits July 21, 2026 17:36
…irect stub

Nests the fourth service under /services/* and takes the searched slug
('career coaching'; 'guidance' is a zero-volume phrase). Safe to move
now: months of 404 plus the homepage-canonical bug left the old URL with
minimal equity. Retitled for 'tech career coach minneapolis', local
hero sentence + Twin Cities FAQ, discovery-call CTAs to /contact,
Service schema + breadcrumb, first-person voice in metadata.

New reusable static-redirect pattern (GitHub Pages cannot 301):
lib/redirects.ts registry + RedirectPage stub emitting an instant meta
refresh with canonical pointing at the destination. The old URL keeps a
stub that is never sitemapped or internally linked; the metadata-policy
test now computes each page's route, requires canonical === route for
real pages, and requires stubs to canonicalize to their registered
destination. e2e verifies the browser lands on the new page.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
/services hub (four offering cards, solo positioning, local intro) plus
dedicated pages for the three consulting lines, each targeting one
primary query: software-consulting ('software consultant minneapolis'),
ai-consulting ('ai consulting minneapolis'), and
engineering-practice-improvement ('engineering process improvement
consultant'). Every page: distinct 600-900 word copy in first person, a
deliberately different local angle (onsite architecture sessions / past-
the-demo-phase AI adoption / embedded practice change), six real-search
FAQs with FAQPage schema, Service schema, breadcrumb, own OG card,
related-services triangle, and discovery-call CTA. The AI page cites the
agent-coordination essay as proof of practice. Sitemap gains all four
routes in the same PR that ships them (health-check contract).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Home becomes the metro category page: 'Twin Cities Software Consulting'
title (keyword-first for a weak-brand domain), 'Software Consulting in
the Twin Cities' h1, founder hero linking /about, discovery-call CTA,
four service cards from a shared config (also consumed by the hub), an
AI-native differentiator section citing the agent-coordination essay,
and exactly one local trust line. /about is the E-E-A-T anchor — Jordan
Winters, Minneapolis, beliefs, solo working model, proof links — and the
Person schema url now points there. /contact ships LinkedIn-primary
(email lands in the gated commit) with the free-discovery-call framing
and the NAP line. Sitemap gains /about and /contact.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Nav becomes home/services/coaching/blog/about/contact (experiments moves
to the footer — 12 of 13 are noindexed stubs). The footer grows three
link columns (Services x4 / Site / Connect) plus one plain NAP line,
fixing the sitewide orphaning of a footer that previously held only
social links. Root default title/description flip to the Twin Cities
positioning (deliberately held out of PR1), authors credit Jordan
Winters, and the WebSite/Organization schema description follows via
siteConfig. Blog: index description drops 'the team' voice; the post's
title tag targets 'Coordinating AI Coding Agents' via a new seoTitle
field (h1 unchanged), gains a byline linking /about, repoints its intro
Engineering-Practice-Improvement link from LinkedIn to the service page,
and closes with an ai-consulting pointer. New AI/feed discovery
surfaces: public/llms.txt and a hand-rolled static /rss.xml (linked from
the layout head and blog index).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
MERGE GATE: requires the Namecheap Email Forwarding alias 'jordan' to
exist and receive a test message first (~5 min in the Namecheap
dashboard; MX/SPF already live). Drop this commit if merging before the
alias is confirmed — everything else in the PR stands alone.

Adds the email (single source: siteConfig.email) to the footer Connect
column, the contact page as the primary CTA, the home closing CTA, the
career-coaching contact section, and the ProfessionalService schema's
email property.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
next-image-export-optimizer rewrites its hashes JSON 4-space-indented on
every build, while Biome's formatter gate demands 2-space — so any local
build broke the next lint run (the PR auto-fix bot and a manual fix had
each patched the symptom once already). Generated build output doesn't
belong in the style gate; the committed copy now matches the optimizer's
own output so builds are no-ops on it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The footer now also links Blog, so the unscoped getByRole('link')
tripped Playwright strict mode. aria-label='Main' on the nav is the
a11y-correct disambiguator.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

🔍 Preview Build Ready

Build Status: ✅ Success
Node Version: 24

📦 Preview artifact uploaded - check the Actions tab to download.

This preview was built from commit d43ebf7

Adversarial content review against the resume and LinkedIn found the
copy drifting from the record and the voice. Corrections:

Facts. 'Nearly 20 years' (8 places) becomes 'over a decade' - the
record starts in 2015 and his own headline says 'a decade.' The stale
'I lead Engineering Practice Improvement for a living' claims move to
past-tense portfolio framing (that role ended with the Thrivent
contract in Jan 2026), and the blog byline drops the role label.
Schema: foundingDate corrects from a fabricated 2024 to 2025-03-01
(LinkedIn), jobTitle becomes Software & Infrastructure Consultant, and
knowsAbout gains the real hard skills (AWS/GCP, Terraform, ECS/K8s,
Datadog/ELK, FinOps, agentic AI).

Positioning. The software-consulting page drops the invented app-shop
narrative ('rescue work is a specialty' had no basis) for the evidenced
specialty mix: 0-to-1 platform foundations, architecture, and
infrastructure modernization with real numbers (70% legacy cost cut,
>75% faster builds/deploys, >$1.5M/yr cloud savings, empty repo to
public beta in six months). The AI page adds its strongest proof (six-
month 0-to-beta with 90% gross margin, regulated-environment LLM
patterns); the practice page cites the founded Observability practice
and the +30%/-50% scrum outcomes. About restores the essay's named
frameworks (Product Operating Model, Agile, DevOps) that an earlier
rewrite had blurred into aphorisms, and adds the real career spine.

Voice. All 86 user-facing em dashes replaced with restructured
sentences or spaced hyphens (his written style; his own essay contains
zero em dashes), including the experiment pages. Seven rhetorical-
question CTAs become declarative, double-dash asides and stacked
antithesis removed, duplicate 'no handoffs' phrasing trimmed. A new
policy test bans em dashes in page sources permanently.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

🔍 Preview Build Ready

Build Status: ✅ Success
Node Version: 24

📦 Preview artifact uploaded - check the Actions tab to download.

This preview was built from commit 5d91d05

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant