insights: coming-soon page; block AI training crawlers; fix stale e2e
CI / ci (push) Successful in 1m34s

- /insights/ is a reader-facing Coming soon page; developer cards removed
- robots.txt: Disallow GPTBot, ClaudeBot, Google-Extended, CCBot,
  Applebot-Extended, meta-externalagent, Bytespider. Search and AI
  answer crawlers stay allowed. Corrects the old Google-Extended
  comment: it does not affect Search or AI Overviews.
- e2e: assert the real headings and the header Services link (48/48)
This commit is contained in:
bmr_bluecap committed 2026-10-23 14:40:51 -04:00
1 parent e07cb8545e
commit 04e0c4d9cd
4 files changed
+43 -61

No files matched your search

+15 -24
View File
@@ -28,7 +28,7 @@ Status legend: `[ ]` open · `[x]` done · `[?]` needs verification · 🛑 owne
| Search | Google Search Console domain property verified; sitemap submitted and fetched by Googlebot | | Search | Google Search Console domain property verified; sitemap submitted and fetched by Googlebot |
| Mail | MXroute. SPF pass, DKIM pass (selector `x`), DMARC `p=none` — mail-tester 9.5/10 | | Mail | MXroute. SPF pass, DKIM pass (selector `x`), DMARC `p=none` — mail-tester 9.5/10 |
| `npm run build` / `typecheck` / `seo:lint` | green, 21 pages | | `npm run build` / `typecheck` / `seo:lint` | green, 21 pages |
| `npm run test:e2e` | 44 passed / 4 failed — stale assertions, see §2.1 | | `npm run test:e2e` | **48/48 green** |
### Traffic path ### Traffic path
``` ```
@@ -79,32 +79,21 @@ discovered 2026-10-23.
- [ ] Durable fix in the `gitea` ansible role: disable live-restore on that VM, or restart the - [ ] Durable fix in the `gitea` ansible role: disable live-restore on that VM, or restart the
runner after dockerd restarts (systemd `PartOf=`/`ExecStartPost` hook). runner after dockerd restarts (systemd `PartOf=`/`ExecStartPost` hook).
### 1.4 `/insights/` ships empty, with developer-facing copy 🛑 ### 1.4 `/insights/` ✅ 2026-10-23
`src/pages/insights/index.astro` shows two cards written for a developer ("Publishing path", Now a reader-facing "Coming soon" page (hero + contact CTA); the developer-facing cards are gone.
"RSS support") and a hero calling the section a "Future publishing home". The section is in the It stays in the nav. When the first real article lands, restore a listing on this page.
primary nav with zero articles.
- [ ] 🛑 Ship it or not.
- [ ] If yes: delete both note-cards and rewrite the hero as reader-facing copy.
- [ ] If no: drop it from the primary nav and exclude the route from the sitemap.
### 1.5 Privacy policy review 🛑 ### 1.5 Privacy policy ✅ 2026-10-23
`/privacy/` went live 2026-10-23, drafted from what the site actually does (contact form via `/privacy/` reviewed and accepted by the owner. No consent banner (owner's call).
Formspree, Umami, GA4 with cookies, server/security logs). Anything added to `src/components/Analytics.astro` must be disclosed on `/privacy/`.
- [ ] 🛑 Owner review of the text. It is a legal document.
- [ ] 🛑 Consent banner: GA4 sets cookies. Strictly, EU/UK visitors need opt-in consent; most
US-focused small-business sites run with a privacy policy only. Owner's call. Umami needs
neither.
- Anything added to `src/components/Analytics.astro` must be disclosed on `/privacy/`.
--- ---
## 2. Should fix ## 2. Should fix
### 2.1 e2e suite is red — stale assertions, not a regression ### 2.1 e2e suite ✅ green 2026-10-23 — 48/48
4 of 48 fail (2 tests × desktop/mobile). Both assert **copy the exact-copy work correctly removed**: The stale assertions (invented copy removed by the exact-copy work) now check the real headings
- `tests/site.spec.ts:63` waits for a link named `Explore services`; the homepage CTA is `All services`. and use the header Services link; the homepage body has no direct `/services/` link.
- `tests/site.spec.ts:93` waits for a heading matching `/Focused expertise/`, gone from `/focus-areas/`.
- [ ] Update both tests to current copy; get back to green so the suite is trustworthy.
### 2.2 Repo hygiene ### 2.2 Repo hygiene
Commit `9f1cbca` committed ~50 `.playwright-mcp/` screenshots/snapshots plus a 2.8 MB Commit `9f1cbca` committed ~50 `.playwright-mcp/` screenshots/snapshots plus a 2.8 MB
@@ -122,11 +111,13 @@ Commit `9f1cbca` committed ~50 `.playwright-mcp/` screenshots/snapshots plus a 2
- [x] **Canonical host** — `www` (2026-10-23). Apex 301s via Traefik middleware in `k8s/ingress.yaml`. - [x] **Canonical host** — `www` (2026-10-23). Apex 301s via Traefik middleware in `k8s/ingress.yaml`.
- [x] **Analytics** — Umami + GA4 side by side (2026-10-23). - [x] **Analytics** — Umami + GA4 side by side (2026-10-23).
- [ ] 🛑 **Contact details** — confirm `804-332-6618` / `info@bluecapstrategies.com` are final. - [ ] 🛑 **Contact details** — confirm `804-332-6618` / `info@bluecapstrategies.com` are final.
- [ ] 🛑 **AI training-bot policy** — `GPTBot` and `ClaudeBot` are **Allow: /** in - [x] **AI training-bot policy** — training crawlers blocked (2026-10-23): GPTBot, ClaudeBot,
`public/robots.txt`, with a ready-to-uncomment `Disallow: /`. The site is now crawlable. Google-Extended, CCBot, Applebot-Extended, meta-externalagent, Bytespider. Search and
AI answer/search crawlers (Googlebot, Bingbot, OAI-SearchBot, Claude-SearchBot, …) stay allowed.
- [ ] 🛑 **Brad Rodgers bio + credentials + portrait** — gates `Person` schema, the About - [ ] 🛑 **Brad Rodgers bio + credentials + portrait** — gates `Person` schema, the About
buildout, and principal-led E-E-A-T/GEO positioning. buildout, and principal-led E-E-A-T/GEO positioning.
- [ ] 🛑 **Real insights articles** — no fabricated or unreviewed AI content (see §1.4). - [ ] 🛑 **Real insights articles** — no fabricated or unreviewed AI content. `/insights/` is
"Coming soon" until the first one exists (§1.4).
- [ ] 🛑 **Social profiles** — provide real profiles or omit the footer block. - [ ] 🛑 **Social profiles** — provide real profiles or omit the footer block.
- [ ] 🛑 **Proof modules / representative engagements** — no fabricated client work. - [ ] 🛑 **Proof modules / representative engagements** — no fabricated client work.
+21 -13
View File
@@ -1,8 +1,6 @@
# BlueCap Strategies robots.txt — see docs/seo-aeo-optimization.md §5 # BlueCap Strategies robots.txt — see docs/seo-aeo-optimization.md §5
# Policy: allow classic search + AI answer/search crawlers for maximum discoverability and # Policy: allow classic search + AI answer/search crawlers for maximum discoverability and
# answer-engine citation. Training-crawler policy (GPTBot, ClaudeBot, Google-Extended) is an # answer-engine citation. Model-TRAINING crawlers (GPTBot, ClaudeBot, Google-Extended, …) are BLOCKED (owner decision, 2026-10-23).
# owner decision — currently ALLOWED for maximum reach. To opt out of model training, uncomment
# the "Disallow: /" lines in the training-crawler block below.
# --- Classic search engines --- # --- Classic search engines ---
User-agent: Googlebot User-agent: Googlebot
@@ -27,19 +25,29 @@ Allow: /
User-agent: PerplexityBot User-agent: PerplexityBot
Allow: / Allow: /
# Google-Extended controls Gemini + Google AI Overviews (this is also a training signal for # --- AI model-training crawlers — blocked (owner decision, 2026-10-23) ---
# Google — treated as answer-visibility here; move to the training block to opt out).
User-agent: Google-Extended
Allow: /
# --- AI model-training crawlers (owner decision — allowed by default) ---
User-agent: GPTBot User-agent: GPTBot
Allow: / Disallow: /
# Disallow: /
User-agent: ClaudeBot User-agent: ClaudeBot
Allow: / Disallow: /
# Disallow: /
User-agent: CCBot
Disallow: /
User-agent: Applebot-Extended
Disallow: /
User-agent: meta-externalagent
Disallow: /
User-agent: Bytespider
Disallow: /
# Google-Extended is Google's opt-out for Gemini training/grounding only. Google Search and AI
# Overviews are governed by Googlebot (allowed above), so blocking this does not affect search.
User-agent: Google-Extended
Disallow: /
# --- Everything else --- # --- Everything else ---
User-agent: * User-agent: *
+3 -20
View File
@@ -5,7 +5,7 @@ import ContactCta from "@components/ContactCta.astro";
import { breadcrumbSchema, pageSchema } from "@utils/schema"; import { breadcrumbSchema, pageSchema } from "@utils/schema";
const description = const description =
"A prepared future publishing section for BlueCap Strategies articles, essays, case studies, market notes, and announcements."; "Articles, analysis, and updates from BlueCap Strategies are coming soon.";
--- ---
<BaseLayout <BaseLayout
@@ -22,26 +22,9 @@ const description =
> >
<PageHero <PageHero
eyebrow="Insights" eyebrow="Insights"
title="Future publishing home for BlueCap Strategies" title="Coming soon"
lead="This section is prepared for articles, essays, case studies, market notes, and announcements when BlueCap migrates thought leadership from Substack, Medium, or other channels onto its own domain." lead="Articles, analysis, and updates from BlueCap Strategies will be published here."
image="/assets/live-site/img_0067.jpg" image="/assets/live-site/img_0067.jpg"
/> />
<section class="insights-shell">
<article class="note-card">
<h2>Publishing path</h2>
<p>
Future posts can be added as Markdown or MDX in <code>src/content/insights</code> with
typed frontmatter for title, description, publish date, modified date, tags, and optional
canonical URL.
</p>
</article>
<article class="note-card">
<h2>RSS support</h2>
<p>
RSS is available at <a href="/rss.xml">/rss.xml</a>. The feed is ready for future article
imports even though no public posts are published yet.
</p>
</article>
</section>
<ContactCta /> <ContactCta />
</BaseLayout> </BaseLayout>
+4 -4
View File
@@ -64,14 +64,14 @@ test("desktop navigation and CTA links are clickable", async ({ page }) => {
await page.setViewportSize({ width: 1440, height: 900 }); await page.setViewportSize({ width: 1440, height: 900 });
await page.goto("/"); await page.goto("/");
await page.getByRole("link", { name: "Explore services" }).click(); await page.getByRole("banner").getByRole("link", { name: "Services", exact: true }).first().click();
await expect(page).toHaveURL(/\/services\/$/); await expect(page).toHaveURL(/\/services\/$/);
await expect(page.getByRole("heading", { name: /Advisory services/ })).toBeVisible(); await expect(page.getByRole("heading", { level: 1, name: "Services" })).toBeVisible();
await page.goto("/"); await page.goto("/");
await page.getByRole("link", { name: "Request a consultation" }).first().click(); await page.getByRole("link", { name: "Request a consultation" }).first().click();
await expect(page).toHaveURL(/\/contact\/$/); await expect(page).toHaveURL(/\/contact\/$/);
await expect(page.getByRole("heading", { name: /Get in touch/ })).toBeVisible(); await expect(page.getByRole("heading", { level: 1, name: "Contact Us" })).toBeVisible();
}); });
test("service and focus-area learn more links are wired to detail pages", async ({ page }) => { test("service and focus-area learn more links are wired to detail pages", async ({ page }) => {
@@ -98,7 +98,7 @@ test("mobile menu opens and navigates", async ({ page }) => {
await expect(page.locator(".mobile-nav nav")).toBeVisible(); await expect(page.locator(".mobile-nav nav")).toBeVisible();
await page.locator(".mobile-nav").getByRole("link", { name: "Focus Areas" }).click(); await page.locator(".mobile-nav").getByRole("link", { name: "Focus Areas" }).click();
await expect(page).toHaveURL(/\/focus-areas\/$/); await expect(page).toHaveURL(/\/focus-areas\/$/);
await expect(page.getByRole("heading", { name: /Focused expertise/ })).toBeVisible(); await expect(page.getByRole("heading", { level: 1, name: "Core Focus Areas" })).toBeVisible();
}); });
test("contact form validates required, invalid, and valid paths", async ({ page }) => { test("contact form validates required, invalid, and valid paths", async ({ page }) => {