Skip to content

chore(docs): promotion and growth groundwork — homepage, SEO, privacy, analytics wiring#454

Merged
marcusrbrown merged 11 commits into
mainfrom
feat/promotion-growth
May 29, 2026
Merged

chore(docs): promotion and growth groundwork — homepage, SEO, privacy, analytics wiring#454
marcusrbrown merged 11 commits into
mainfrom
feat/promotion-growth

Conversation

@marcusrbrown
Copy link
Copy Markdown
Owner

Summary

Promotion-and-growth groundwork for the docs site: a product-landing homepage with live counts, structured SEO, a privacy page, privacy-respecting analytics wiring (kept disabled until launch), and drafts for the public announcement channels.

This is the docs-site and growth-instrumentation slice. The live before/after demo, the OpenCode ecosystem listing, and the public launch are intentionally held back as separate steps.

What's included

  • Homepage rewrite — product-landing structure: hero with value proposition, a live stats banner (skills/agents/components, sourced from the registry at build time), the brainstorm → plan → work → review workflow, and an honest "what it is not" section.
  • Live-counts emitterdocs/scripts/generate-stats.ts reads registry/registry.jsonc and writes docs/src/data/stats.json during docs:generate, so the homepage numbers never drift. The artifact is generated, not committed.
  • SEO metadata — JSON-LD SoftwareSourceCode, Open Graph, and Twitter card tags, plus a sharper site title and description.
  • Privacy page + footer link — documents the analytics posture: self-hosted, cookie-free, honors Do Not Track, no plugin-runtime telemetry.
  • Analytics wiring — the Umami tag is wired into the docs head but stays disabled unless UMAMI_WEBSITE_ID is set in the deploy environment, so dev and preview builds never emit analytics.
  • Announcement draftsdocs/promotion/ holds working drafts for the ecosystem listing, LinkedIn, Bluesky, and the OpenCode Discord. Drafts only; counts are placeholders pending a pre-publish refresh.

Testing

  • bun run typecheck — clean
  • bun run lint — clean
  • bun test tests/unit — 927 pass / 0 fail
  • bun scripts/content-integrity.ts — clean
  • bun run registry:drift — up to date (103 components)
  • bun run docs:build — 111 pages, complete

Release impact

All commits are chore(docs): / test(docs): / docs(plans): — none touch the published package surface (dist/, skills/, agents/), so per .releaserc.yaml this does not trigger a version bump. The docs site deploys on the next release or via manual workflow_dispatch.

After merge

Analytics stays dark until the launch step sets UMAMI_WEBSITE_ID in the deploy environment — merging this PR collects no data. The before/after demo guide, the ecosystem PR, and the public launch (with its adoption gate) follow as separate steps.

@marcusrbrown marcusrbrown changed the title feat(docs): promotion and growth groundwork — homepage, SEO, privacy, analytics wiring chore(docs): promotion and growth groundwork — homepage, SEO, privacy, analytics wiring May 29, 2026
Copy link
Copy Markdown
Collaborator

@fro-bot fro-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: PASS

Everything looks solid. The generate-stats pipeline is cleanly architected — pure generateStats function + injected resolveGitTag dependency make it fully testable without mocking the filesystem, and the tests cover all the meaningful paths (happy path, placeholder fallback, unreleased fallback, all three throw paths, idempotence). No any, no @ts-ignore, node: protocol throughout, explicit return types on all exports. The Umami wiring is correctly hard-disabled until UMAMI_WEBSITE_ID is set — dev and CI builds emit no analytics script at all.

Blocking issues

None.

Non-blocking concerns

  1. stats.json build-order couplingStatsBanner.astro imports ../data/stats.json at build time. If someone runs bun run --cwd docs build directly (skipping docs:build), the import will fail with a missing-module error rather than a graceful "counts not generated yet" message. The docs:build script correctly gates on docs:generate first, so CI is safe, but the error surface for local contributors who bypass the wrapper is poor. A note in the contributing docs or a cleaner build-time error would help.

  2. Privacy page retention note is incompletedocs/src/content/docs/privacy.mdx contains a blockquote noting that retention and IP-handling details are "pending review of the live instance configuration and will be added here before the public launch." This is a committed public-record stub. Since analytics is hard-disabled until launch anyway, this is low urgency, but it should be resolved before UMAMI_WEBSITE_ID is set in the deploy environment — not treated as optional polish.

  3. countAgents counts all .md files recursively — this is a deliberate design choice (documented in the function JSDoc) but it means any future .md non-agent files dropped under agents/ would silently inflate the homepage count. countSkills uses the SKILL.md sentinel as a guard; countAgents has no equivalent. No current problem, but worth knowing.

  4. CustomFooter.astro uses a hardcoded /systematic/ base<a href="/systematic/privacy/">. If the Starlight base ever changes, this breaks silently. Minor, since the base is set in astro.config.mjs and is unlikely to change.

Missing tests

None — the 9-test suite for generate-stats.ts covers the full boundary surface. The happy path, version resolution branches, and all three error throw paths are tested. serializeStats is exercised by the idempotence test.

Risk assessment: LOW

  • No changes to the plugin runtime, config schema, dist output, or any published package surface. This is purely docs-site and build tooling.
  • The analytics script is conditionally emitted only when UMAMI_WEBSITE_ID is set — the current deploy environment does not set it, so merging this PR collects no data and changes no user-facing behavior beyond the homepage content rewrite.
  • The stats artifact is generated and gitignored; it cannot go stale in-repo, and the build will loudly fail if the registry is missing or malformed.
  • Blast radius of a regression: docs site homepage shows wrong counts or build fails. Both are caught by docs:build in CI before deploy.

Run Summary
Field Value
Event pull_request
Repository marcusrbrown/systematic
Run ID 26651850196
Cache hit
Session pr-454

Build-time generator walking skills/, agents/, and registry/registry.jsonc
to emit docs/src/data/stats.json (skills/agents/components/version), wired
into docs:generate before astro build. Version resolves from the latest git
tag when package.json holds the semantic-release placeholder, falling back to
'unreleased' so the homepage never renders a broken version string.

Includes the tracked promotion-and-growth plan doc.
Conditional Umami script head entry in astro.config.mjs, emitted only when
UMAMI_WEBSITE_ID is set so dev/preview builds and the current static site ship
no tracker. Install CTAs on the home and quick-start pages carry
data-umami-event=click_install_cta attributes (inert until the script loads).
Emits the full deployed tag shape: defer + data-do-not-track + data-exclude-search
+ data-exclude-hash.
docs/src/data/stats.json is emitted by generate-stats.ts during docs:generate
(which docs:build runs first), so it must not be tracked — a committed copy
goes stale the moment a skill/agent is added. Matches the existing
generated-reference-page and schema-mirror ignore entries.
Documents the cookie-free, self-hosted, DNT-respected, no-PII analytics
posture and restates the plugin-runtime no-telemetry guarantee. CustomFooter
renders Starlight's default footer and appends the Privacy link.
Retention/IP specifics marked pending until reconciled against the live config.
Product-landing structure (hero, live StatsBanner, demo proof CTA, feature
grid, value props, quick start, honesty section) using Systematic theme
tokens — no shader, no React. StatsBanner reads live counts from the
generated stats artifact; install CTA retains its activation-event attribute.
og:type, og:site_name, twitter:card/image, and a JSON-LD SoftwareSourceCode
block in the docs head, plus a sharper site title and capability-focused
description (no hardcoded counts). Built via JSON.stringify so the structured
data can't carry trailing-comma errors.
Evergreen drafts for LinkedIn (project + post), Bluesky, and the OpenCode
Discord under docs/promotion/, plus a README documenting voice rules, the
placeholder fill step, and the permanent-record caveat. Count claims use
{{SKILLS}}/{{AGENTS}}/{{COMPONENTS}}/{{VERSION}} placeholders filled from the
generated stats artifact at post time, so numbers can't silently go stale.
generate-stats.test.ts was co-located under docs/scripts/, where the unit
suite (bun test tests/unit) never picked it up. Relocate it alongside the
other generator tests (mirrors generate-config-reference.test.ts, which tests
a docs/scripts/ generator from tests/unit/) and repath the import. Unit count
918 -> 926.
Harden the stats emitter and tighten its tests after code review:
- Fail loud on malformed registry JSONC (pass an errors array to the parser
  and throw), and narrow parsed JSON through an isRecord guard instead of
  casting to trusted shapes.
- StatsBanner renders the 'unreleased' fallback verbatim instead of a bogus
  'vunreleased'.
- Idempotence test exercises the production serializeStats; fail-loud tests
  assert the specific diagnostics; add a malformed-JSONC case.
- Privacy page describes Quick Start as an aggregate pageview, not a distinct
  named event (only click_install_cta is a custom event).
The "See the Difference" CTA pointed at a with/without comparison page that
doesn't exist yet. Retarget it to the existing main-loop guide so the homepage
has no dead link; the section can be upgraded to a dedicated comparison page
later.
U1/U2/U4/U5/U6/U8 shipped; U3 (demo), U7 (ecosystem PR), and U9 (public
launch + gate) remain deferred.
Copy link
Copy Markdown
Collaborator

@fro-bot fro-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving — the review posted in this thread (run 26651850196) is current. No new commits since that review; verdict and findings are unchanged.

Verdict: PASS

Blocking issues

None.

Non-blocking concerns

  1. stats.json build-order couplingStatsBanner.astro imports ../data/stats.json at build time. Direct bun run --cwd docs build bypasses docs:generate and will fail with a missing-module error. CI is safe (docs:build gates correctly); this only affects contributors who bypass the wrapper.

  2. Privacy page retention note is incomplete — the blockquote in privacy.mdx noting retention/IP-handling details are pending must be resolved before UMAMI_WEBSITE_ID is set in the deploy environment.

  3. countAgents has no file-type sentinel — all .md files under agents/ are counted; a future non-agent .md there would silently inflate the homepage stat. countSkills uses SKILL.md as a guard; countAgents does not.

  4. CustomFooter.astro hardcodes /systematic/ base — breaks silently if the Starlight base changes.

Missing tests

None.

Risk assessment: LOW

No changes to the plugin runtime, config schema, dist output, or any published package surface. Analytics is hard-disabled (script omitted entirely) until UMAMI_WEBSITE_ID is set — merging collects no data. Blast radius of a regression is limited to the docs site homepage, caught by docs:build in CI before deploy.


Run Summary
Field Value
Event pull_request
Repository marcusrbrown/systematic
Run ID 26652909848
Cache hit
Session ses_18b3bfa20ffe2QS4rv0yTIPPOe

@marcusrbrown marcusrbrown merged commit 6e9fcc5 into main May 29, 2026
11 checks passed
@marcusrbrown marcusrbrown deleted the feat/promotion-growth branch May 29, 2026 17:49
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.

2 participants