Skip to content

merge (#1)#1278

Open
guneyunus wants to merge 1 commit into
fullstackhero:mainfrom
guneyunus:develop
Open

merge (#1)#1278
guneyunus wants to merge 1 commit into
fullstackhero:mainfrom
guneyunus:develop

Conversation

@guneyunus
Copy link
Copy Markdown

  • build(migrator): chiseled Dockerfile + APP_UID + csproj container hardening

  • build(deploy): docker-compose .env.example with all knobs documented

  • feat(docs): redesign landing — conversion-focused with modern code window

Replaces the placeholder hero/feature-grid with a six-section landing designed for developer conversion:

  • Hero: editorial display headline with gradient-text inflection, single strong subhead, dual CTA, immediate install command (copyable) below the fold. Brand-shadow blur + subtle dot-field for atmosphere.
  • CodeFirst: split section with editorial copy left, custom-framed code window right showing real RegisterUser feature folder (endpoint / handler / validator tabs). Mac-style traffic lights, green-accent active tab underline, hover gradient border, status footer.
  • WhatsIncluded: 12-item refined checklist (not boxy cards). Scannable.
  • ModuleShowcase: 3 deep cards (Identity / Multitenancy / Auditing) — tagline, capability bullets, real code snippet, deep-dive link each. Retains magnetic-shimmer hover from blog token system.
  • TechStack: 12-tech compact grid with vertical border accent on hover.
  • FinalCta: editorial close with repeated install command + CTAs + '≈5 minutes to localhost' time-to-running indicator.

Codeblocks use Expressive Code's programmatic component with frame=none so the custom window chrome (traffic lights, tabs, status bar) wraps clean EC output. EC config externalized to ec.config.mjs as required by the programmatic path.

Every color/surface uses semantic tokens — no hardcoded colors except mac traffic light hexes (intentional, vendor-recognizable signal). Cleanly inverts between light and dark.

  • build(deploy): postgres init SQL — required extensions

  • build(deploy): production docker-compose.yml — full stack on one host

  • docs(deploy): five-minute docker-compose deploy guide

  • fix(docs): theme toggle, nav active, alignment, code panel polish

Batch of bug fixes from initial review pass:

  • ThemeToggle: switched script to is:inline plain JS (was hoisted module with TS annotations). Dropped View Transitions wrapper — was the silent failure mode. Listener now attaches deterministically.
  • Header nav active state: previous startsWith() matched /docs/ for any /docs/* path, lighting up both 'Docs' and the actual section. New logic picks the longest matching prefix as the single active item.
  • Container width: header + footer were max-w-6xl while docs grid was max-w-7xl, causing visible left/right misalignment on docs pages. Unified all surfaces to max-w-7xl.
  • prose.css ul bullets: referenced ../icons/svgs/star.svg from the blog that we never copied → runtime 404. Replaced with a green dot using --primary token (consistent with our design).
  • Dropped @astrojs/react integration: zero React components remain after ThemeToggle was converted to Astro. Eliminated the Vite 7 rolldown 'Missing field moduleType' dev errors from react-refresh.
  • Code panel rewrite (code-window.css): now targets the real DOM (.expressive-code .frame, not the outer div). Soft elevation, brand hover border, language badge floats above pre on hover, copy button positioned cleanly inside header. Mac traffic lights (already from EC) sit naturally in the gradient title bar.
  • fix(docs): use blog's exact MDX codeblock style (drop custom code-window.css)

The blog's MDX codeblock treatment lives in prose.css and is minimal: rounded-xl pre, near-invisible white-with-low-opacity border, brand-tinted border on hover, copy button scales 1.05 with brand-tinted bg on hover, plus a gradient line-highlight indicator. My custom code-window.css was overdesigning a problem the blog had already solved.

  • Deleted docs/src/styles/code-window.css
  • Removed its import from globals.css
  • prose.css already inherited from the blog verbatim; updated the two blog-purple references: rgb(118 89 236, ...) → rgb(22 163 74, ...) (#16a34a, our --primary-soft mid-green) and #4bf3c8 → #4ade80 (our dark-mode --primary-soft) in the line-highlight gradient.
  • fix(deploy): resolve 3 issues found during e2e smoke test
  • docker-compose migrator: add --seed flag so IdentityDbInitializer runs SeedAdminUserAsync and admin@root.com is seeded on first boot
  • docker-compose api: add AllowedHosts=* env var to override the appsettings.Production.json restriction (was api.example.com, which Kestrel rejected for localhost requests)
  • docker-compose api: add HangfireOptions__UserName/Password env vars (required by ValidateOnStart() in Jobs/Extensions.cs); document in .env.example with generation instructions
  • appsettings.Production.json: set AllowedHosts to * (host filtering belongs at the reverse proxy layer, not Kestrel)

All surfaces verified: health/live, health/ready (all 14 checks Healthy), admin config.json, dashboard config.json, both 200 OK, admin@root.com login returns JWT, profile returns correct email. Volume durability: migrator re-runs idempotently on second boot.

  • test(deploy): end-to-end docker-compose smoke verified locally

All 7 steps passed: stack up, migrator exit 0, health Healthy, admin/dashboard config.json correct, login + profile verified, volume durability confirmed, stack torn down and .env removed.

  • docs: point README at deploy/docker for the production deploy story

  • feat(docs): redesign CodeFirst as VS Code-style editor with file tree

Shows the full VSA feature-folder structure as a real-feeling IDE pane:

  • Title bar: mac traffic lights, breadcrumb path (Modules.Identity / Features / v1 / Users / RegisterUser / .cs), C# lang badge
  • Left column (file tree): the Users/ folder with RegisterUser/ expanded showing its three files, and five other collapsed feature folders (LoginUser, AssignUserRole, ChangePassword, DeleteUser, RefreshToken) — visually demonstrates that each feature is its own folder with its own files
  • Active file: green left-border accent + tinted background + primary text color. Inactive: muted with hover row tint
  • Right column: code panel with EC frame=none (chrome stripped, just the syntax-highlighted body)
  • Status footer: project name, encoding, line count updates with selection
  • Breadcrumb's filename and line count both swap on click
  • Hover gradient ring behind the editor (mask-composite trick)
  • Mobile: tree stacks above code with bottom border instead of right
  • Indentation guide line (1px vertical) inside the RegisterUser folder
  • Chevrons + folder icons via inline SVGs (lucide-shaped) — no astro-icon dep needed for the section
  • docs(spec): editorial alignment of docs site with codewithmukesh/blog

Spec for the polish pass that ports the blog's brand/* primitive system (Button, Card, Callout, Pill, Kbd) into the docs site, adds the eyebrow numbering pattern, wires magnetic-cards + animated-IDE patterns, and aligns ExpressiveCode theme. Green stays as the FSH brand color.

  • fix(container): use numeric UID 1654 in

MSBuild does not expand shell-style variables in , so the literal string "$APP_UID" was written into the OCI image config's User field by dotnet publish /t:PublishContainer. At docker run time the daemon could not resolve that "user" and exited with unable to find user $APP_UID: no matching entries in passwd file, breaking the DbMigrator Container Smoke job in CI.

The hand-written Dockerfiles keep USER $APP_UID because Docker's parser does expand ENV vars inside the USER directive at build time.

  • feat(docs): editorial alignment with codewithmukesh/blog

Ports the blog's brand primitive system (Button, Card, Callout, Pill, Kbd) into the docs site and adopts the magazine-style numbered eyebrow rhythm across the six landing sections. Green stays as the FSH brand color; primitives flow through var(--primary) so the swap is automatic.

  • brand/ primitives ported verbatim with var(--primary) substitutions in Card.astro (color-mix on hover glows/borders).
  • New SectionEyebrow primitive: mono uppercase tabular-nums "01 · …" pattern with a primary-tinted accent rule.
  • New AnimatedIde wrapper: pointer-tracked 3D tilt + cursor-following radial spotlight via --mouse-x / --mouse-y custom properties; wraps the CodeFirst editor. No-op on touch / reduced-motion.
  • Magnetic-cards script ported into docs/src/scripts/ and wired from BaseLayout. Handles both [data-magnetic-card] and [data-magnetic-ide].
  • ExpressiveCode config extracted into ec.config.mjs (function-valued options aren't JSON-serializable when inlined). Houston dark theme + green-tinted frame/marker accents.
  • Hero CTAs + FinalCta CTAs + Header GitHub button now use the Button primitive (ink-on-paper primary). Header Ctrl·K hint uses .
  • Hero gains a tabular-nums proof strip (".NET 10 · 3 modules · 598 tests · 5 min to localhost") + mono "$ get running locally" prompt.
  • Prose list marker swaps from a dot to the star SVG used by the blog.

Build green; 10 pages built in 2.13s.

  • feat(docs): redesign Hero with animated terminal + stat cards

Replaces the install-and-CTA-focused hero with a magazine-style conversion surface modeled on the dotnet-claude-kit hero from codewithmukesh/blog.

What changed

  • Magazine eyebrow rule across the top: "Open source · .NET 10 starter kit ─── v1 · MIT · production-ready" (tabular-nums mono, primary on the left, muted on the right, hairline rule between).
  • Display headline now splits font-light lead-in ("The .NET starter kit") from font-bold payoff ("that actually ships.") with a green gradient underline strip beneath the bold phrase.
  • Subtitle bolds the three pillars (multitenancy, identity, observability) and lands on a feature-work-not-scaffolding payoff.
  • Trust strip below the CTAs: v1 · MIT licensed · .NET 10 / C# 14 · PostgreSQL · Redis · React.
  • Animated terminal preview: typewriter for the dotnet run command, then staggered reveal of an Aspire orchestration boot sequence (PostgreSQL, Redis, API, Dashboard, Hangfire) ending in "Ready in 4.2s · 5 services orchestrated · multitenancy enforced". The blinking emerald cursor animates while the command types out.
  • "What ships in v1" stat-card grid (4 metric cards): 3 modules · 12+ building blocks · 598 tests · 5 min to localhost. Each card has a large tabular-nums display number with a tinted unit superscript, a hover corner glow, and an EQ-tick bar that "dances" on hover.

Motion and accessibility

  • All animations gated on prefers-reduced-motion: the typewriter jumps to its full text immediately, line reveals fire without delay, and the cursor blink + tick dance are disabled.
  • Terminal cursor + tick dance are CSS keyframe animations, GPU- composited via transform/opacity only.
  • Script is idempotent (data-hero-init guard) so it survives astro:after-swap from Astro view transitions.

Build green; 10 pages built in 1.90s.

  • feat(docs): header nav — add Home, drop Reference

Order is now Home · Docs · Modules · Recipes (down from 4 sections that put Docs first and included a stubbed Reference link). The liquid hover indicator and longest-prefix active-state logic both work unchanged.

  • feat(docs): wire FullStackHero logo + full favicon set

Header brand link now leads with the 512×512 FSH logo (rendered at 28×28, rounded corners) ahead of the lowercase "fullstackhero" mark and the "/docs" mono suffix.

BaseLayout serves the full favicon ladder so every browser and OS gets a sensible icon:

  • favicon.ico (broad fallback, 48×48 multi-res)
  • favicon.svg (modern vector)
  • favicon-32x32.png + favicon-16x16.png (older PNG ladder)
  • logo-fullstackhero.png as the apple-touch-icon

fullstackhero.svg is left in public/ for ad-hoc use even though it's not wired by default.

  • feat(docs): drop "/docs" suffix from header brand link

Logo + lowercase fullstackhero wordmark only now — the /docs mono suffix made sense before there was an icon, but the visual rhythm is cleaner without it.

  • feat(docs): unify all landing sections under Hero's design language

Brings CodeFirst, WhatsIncluded, ModuleShowcase, TechStack, and FinalCta into the same magazine-style chrome as the Hero — magazine eyebrow rule, light/bold split headlines with a green gradient underline strip, inline- bold subtitles, and small editorial flourishes per section.

What landed

  • SectionEyebrow refactored from inline pill → full-width magazine rule (left primary mono label, hairline rule, optional right meta tag).
  • All section containers aligned to max-w-7xl with px-4 sm:px-6 lg:px-8 (matches the Header so brand logo and section content land on the same vertical lines).

Section-by-section

  • 01 CodeFirst: light/bold split "One feature. / One folder." Mini-stat micro-readout (4 files / feature · 0 project jumps · 1 endpoint / slice) ahead of the bullet list, which now uses the star marker SVG. VS-Code editor + AnimatedIde wrapper retained.
  • 02 WhatsIncluded: "Everything you'd build anyway" with a 3-col grid of 12 building-block cards. Each card: icon chip + mono category tag (AUTH, ISOLATION, DATA, …) + title + body, with a corner-glow hover matching the Hero stat cards.
  • 03 ModuleShowcase: "Opinionated where it matters." Three large cards with icon chip + "MODULE" mono label, tagline, star-marker bullets, and a small dark terminal-style code panel (matching the Hero terminal aesthetic — bg #0d1117, three traffic-light dots, mono code).
  • 04 TechStack: 4-col grid of compact tech cells with icon chip, name, version + sub. Hover adds a brand-coloured left accent that slides in.
  • 05 FinalCta: "Start where you'd finish." Dotted backdrop, centred headline, mono "$ clone the starter kit" prompt over the CopyCommand, primary + secondary CTAs, trust strip footer.

All animations are pointer + prefers-reduced-motion aware; magnetic-card tilt still binds idempotently after astro:after-swap.

Build green; 10 pages built in 2.02s.

  • feat(docs): SEO/GEO foundation + fact-checked accuracy pass

Comprehensive SEO/GEO pass aimed at making the docs site the canonical result for ".NET 10 starter kit" type intents, plus a ground-up fact-check against the actual repository. Every claim on the landing now matches what's in the codebase.

ACCURACY FIXES (fact-check against repo)

  • Hero stat cards: 3 modules → 10 (Identity, Multitenancy, Auditing + Files, Chat, Notifications, Webhooks, Billing, Catalog, Tickets); 598 tests → 900+ (actual [Fact]+[Theory] count is ~906); "5 min to localhost" → "1 command to run" with dotnet run boots the stack.
  • Hero animated terminal: rewritten to mirror AppHost.cs exactly — 7 services (postgres, redis, minio, db-migrator, fsh-api, fsh-admin, fsh-dashboard). Removed fictional "Ready in 4.2s" timing claim.
  • TechStack: Aspire 10.0 → 13.3, EF Core → 10.0.8, Postgres → 17, Mediator → 3.0, FluentValidation → 12.1, Scalar → 2.14, Redis StackExchange 2.11, OTel 1.15, Finbuckle 10.0, React TS 5.7 — all pinned to src/Directory.Packages.props.
  • ModuleShowcase Identity: removed "Per-tenant SSO hooks" (not shipped, only JWT bearer auth is in the repo) → replaced with "Rate-limited login, register & password reset" which is real (auth rate-limit policy applied across those endpoints).
  • ModuleShowcase: section eyebrow "3 bounded contexts" → "10 modules ship in v1"; headline reframed as "Three pillars, seven more in the box"; added a four-column aux-module strip below the three pillar cards showing Files, Chat, Notifications, Webhooks, Billing, Catalog, Tickets with one-line summaries.

NEW SECTIONS (fact-checked content only)

  • 05 · WhoItsFor — two-column "Built for / Probably not for" panel. Built for: SaaS-on-.NET teams, VSA + sane-defaults teams, indie devs, founders who want to own their code. Not for: tiny CRUD, Clean-Architecture purists, microservices-day-one, paid-support needs. Reduces wrong-fit bounce + builds trust.
  • 06 · FAQ — 8-question disclosure list answering the questions developers actually ask (free?, prod-ready?, vs ABP?, vs Clean Architecture templates?, do I have to use all 10 modules?, SaaS?, deployment?, update path?). All answers fact-checked.

SEO/GEO INFRASTRUCTURE

  • public/robots.txt — explicit Allow for every major AI/search crawler (GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot, anthropic-ai, PerplexityBot, Perplexity-User, CCBot, cohere-ai, Bytespider, Google-Extended, Applebot-Extended, Bingbot, plus the usual social unfurlers). Points at the sitemap.
  • public/llms.txt — fact-dense kit summary (architecture, 10 modules, 12 building blocks, tech stack with versions, how to run, docs map, positioning vs ABP / Clean Architecture / commercial competitors). Designed to be the single source AI crawlers cite when summarizing what the kit is.
  • JSON-LD structured data wired through BaseLayout.astro using schema.org's @graph: Organization (with logo + sameAs to GitHub + codewithmukesh), WebSite, and SoftwareSourceCode (programmingLanguage, runtimePlatform, codeRepository, MIT license, author).
  • FAQPage JSON-LD attached to the FAQ section so AI engines can extract the Q&A pairs directly without scraping the disclosure markup.
  • theme-color meta tags (light: brand green #15803d, dark: surface #0d0e11) for mobile browser chrome.
  • og-default.png was 404'ing — pointed ogImage at logo-fullstackhero.png so social shares get a real image until a dedicated 1200×630 OG card is designed.

H2 + EYEBROW KEYWORD TIGHTENING

  • Hero H1: "The .NET starter kit" → "The .NET 10 starter kit"; subtitle rewritten as a self-contained citable paragraph that opens "FullStackHero is a free, MIT-licensed, production-ready .NET 10 modular monolith…".
  • Section eyebrows tightened with literal target-keyword right-meta: "Modular Monolith + VSA", "12 shared building blocks", "10 modules ship in v1", ".NET 10 · EF Core 10 · Aspire 13".
  • WhatsIncluded label: "Everything pre-wired" → "Everything a production .NET 10 API needs".

META + DOCUMENT

  • siteConfig.title: "FullStackHero — .NET Starter Kit" → "FullStackHero — Free .NET 10 Starter Kit" (adds the keyword that gets searched).
  • siteConfig.description rewritten to be the same citable paragraph as the hero subtitle, with ".NET 10", "free", "MIT-licensed", "ten modules" front-loaded.

Build green; 10 pages built in 2.18s. robots.txt + llms.txt served correctly on the dev server.

  • feat(docs): copy rewrite — developer-centric + conversion-focused

Tightens copy across every landing section for three audiences (devs, tech leads, CTOs), weaves target search phrases naturally, and pushes the voice closer to "real codebase, not marketing." All claims still match the repo per the fact-check from the previous commit.

Hero

  • H1 bold: "that actually ships." → "you'd actually inherit." Speaks to devs ("I won't fight this"), TLs ("team can take it over"), and CTOs ("low risk, no lock-in") in three words.
  • Subtitle rewritten as a citable definition paragraph: "FullStackHero is the free, MIT-licensed .NET 10 starter kit for teams shipping production SaaS. Ten modules — identity, multitenancy, auditing, files, chat, notifications, webhooks, billing, catalog, tickets — wired through a modular monolith with Vertical Slice Architecture. A real codebase, not a tutorial. No vendor framework. No lock-in." High keyword density, AI-quotable as a single block.
  • Stat-card 4 label: "Command to run" → "Command"; note now ends in "Aspire 13" instead of "Aspire" for the version anchor.

CodeFirst (01)

  • Subtitle: leans harder on the workflow promise — "Add a feature in one PR; ship in one merge." Adds tests to the file list. Calls out "No jumping between five projects" in bold.

WhatsIncluded (02)

  • Subtitle: lists the unglamorous parts by name ("Authentication. Authorization. Migrations. Caching. Background jobs. Structured logging. Distributed tracing. Idempotency. Webhooks.") and lands on "You'd build all of it before your first real feature." More specific, less abstract.

ModuleShowcase (03)

  • Subtitle: tightened with bold "three questions every B2B SaaS has to answer" and "what makes your product yours — not chat, not billing, not file uploads." Frames the seven aux modules as time saved.

TechStack (04)

  • Added a real H2 + subtitle ahead of the grid: "First-party Microsoft. / Best-in-class OSS." with the no-lock-in pitch ("No proprietary framework. No DSL. No magic. The same .NET 10, EF Core 10, Aspire 13, and OSS libraries your team already knows — chosen carefully and wired together so they actually compose."). Targets the CTO/TL "what are we adopting?" audit.

FAQ (06)

  • Added two questions that close common gaps:
    • "How does it scale as my product grows?" — frames the modular monolith → extract-a-service path honestly.
    • "Can I deploy it to Azure, AWS, or Kubernetes?" — confirms each service is a normal Docker image and lists target platforms.

FinalCta (07)

  • Subtitle: "scaffolding" → "plumbing"; adds "run one command" to reinforce the Aspire single-command boot story.

Build green; 13 pages built in 2.68s.

  • feat(docs): redesign sidebar + TOC as numbered chapter cards

Sidebar: warm-paper card per category with single-expand accordion, monospace 01–13 indices, primary-tinted rail + pill on active item, auto-expand for the active chapter. Hidden scrollbar with edge-fade affordance.

TOC: matching card chrome, vertical hairline rail with station-marker dots on h2, scrollspy via rAF-throttled scroll listener, soft halo on active dot.

  • feat(docs): restructure sections + add full Getting Started flow

Sections now: Getting Started, Architecture, Modules, Building Blocks, Cross-Cutting Concerns, Security, Frontend, CLI, Testing, Guides, Deployment, Contributing, Changelog. Drop concepts/reference (folded into Architecture and Changelog), rename recipes → guides.

Getting Started contains Introduction (moved from /docs/), Prerequisites, Quick Start, Install. /docs and /docs/getting-started redirect to the introduction. Header, footer, and landing CTAs rewired to the new paths.

  • feat(docs): tighten typography to docs-standard scale

Page H1 32px/600 (was 36px/700), description 15.5px/lh1.55 (was 18px). Prose H2 22px/600 (was 40px/300), H3 17px/600, H4 15px/600. Body 15px with lh 1.65 (was 18px/1.75). Matches Stripe/Linear/Anthropic norms; weight 600 throughout instead of 700.

  • fix(docs): switch wrangler to Workers Static Assets

Replace deprecated pages_build_output_dir with [assets] block so wrangler deploy works (Pages is being unified under Workers). Rename worker to 'fullstackhero'. Single-quote style to match Cloudflare's auto-PR generator.

  • feat(docs): lowercase fullstackhero brand + GitHub stars on landing

Brand:

  • New title: 'fullstackhero - .NET 10 Starter Kit with React UI'.
  • Wordmark lowercased everywhere except NuGet package IDs (FullStackHero.CLI, FullStackHero.NET.StarterKit must stay PascalCase).
  • JSON-LD Organization + SoftwareSourceCode updated.

Social proof:

  • helpers/github-stars.ts: build-time fetch of stargazers_count with a 6500 fallback for offline / rate-limited builds. Memoized per build.
  • Header GitHub button: split-pill '[gh] GitHub | ★ 6.5k'.
  • Hero chip leads with a tinted star-count pill.
  • Hero secondary CTA: 'Star on GitHub | ★ 6.5k' split-pill.
  • Primary CTA points to /docs/getting-started/quick-start/.
  • feat(docs): mobile docs nav with slide-up sheet

Hidden on lg+, but on phones/tablets the docs main column now has a trigger pill at the top showing the current section. Tapping opens a bottom sheet (Stripe/Vercel/Linear pattern) that slides up from the viewport edge with the full Sidebar inside — same numbered chapter cards and single-expand accordion.

Affordances:

  • Drag handle, close button, tap-scrim to close, ESC to close.
  • Tap any link inside → close (route change feels native).
  • Active page link is scrollIntoView({block: 'center'}) on open.
  • Body scroll lock, inert when closed, focus returns to trigger.

Sidebar now takes a prefix prop (default 'fsh', mobile passes 'fsh-m') so aria-controls / labelledby IDs stay unique across the two simultaneous renderings.

  • fix(docs): drop placeholder favicon.svg — use brand PNGs/ICO

The favicon.svg in public/ was the old 'FSH on green square' placeholder. Removing it and dropping its link from BaseLayout so browsers resolve favicon.ico → 32x32.png → 16x16.png (already the new brand triangle from the earlier favicon commit).

  • feat(docs): tier-1 SEO + GEO code wins

Six concrete improvements aimed at unlocking AI citability and search ranking on top of the editorial landing already in place.

  1. Per-page SEO meta (content.config.ts + DocsLayout)
    Extends the docs content schema with an optional seo block:
    { title, description, keywords, ogImage, noindex }. Each field
    falls through to the frontmatter / site defaults so existing pages
    keep working. The [...slug] route now plumbs seoTitle and
    additionalSchemas through DocsLayout → BaseLayout so individual
    docs pages can target their own keyword-rich <title> independent
    of the on-page H1.

  2. SoftwareApplication schema upgrade (BaseLayout)
    Swapped the homepage's SoftwareSourceCode entity for the more
    AI-citable SoftwareApplication, with applicationCategory
    "DeveloperApplication", operatingSystem, softwareVersion 10.0.0-rc.1,
    softwareRequirements (.NET 10 SDK, PostgreSQL 17, Redis 7, Node 20+),
    downloadUrl, and crucially:
    offers: { price: "0", priceCurrency: "USD", availability: InStock }
    That's the marker Google uses to label software listings as "Free"
    in rich results.

  3. BreadcrumbList JSON-LD on every docs page (helpers/breadcrumb.ts)
    New buildBreadcrumbItems + buildBreadcrumbSchema helpers derive
    breadcrumbs from the URL pathname, title-casing each segment, and
    override the final crumb name with the page's actual title.
    Emitted via additionalSchemas alongside a TechArticle schema
    carrying headline, description, dateModified / datePublished, image,
    author, publisher, and inLanguage — generic enough to cover
    guides, references, concepts, and recipes.

  4. llms-full.txt generated at build time (pages/llms-full.txt.ts)
    New static endpoint that concatenates every docs collection entry
    as plain text — title, blockquote description, canonical URL, raw
    markdown body. Per-page sections separated by --- rules so AI
    crawlers can ingest the entire docs corpus in a single fetch.
    Builds 429-line file at /llms-full.txt covering 16 pages.

  5. Sitemap priorities + changefreq (astro.config.mjs)
    serialize() callback on @astrojs/sitemap sets per-URL priority +
    changefreq:
    / → 1.0 / weekly
    /docs/getting-started/* → 0.9 / monthly
    /docs/modules|building-blocks|architecture/* → 0.8 / monthly
    /docs/guides/* → 0.75 / monthly
    /docs/security|deployment|frontend|testing|cli/* → 0.7 / monthly
    /docs/changelog/* → 0.5 / weekly (freshness signal)
    /docs/contributing/* → 0.4 / yearly
    /docs/* → 0.6 / monthly (fallback)

  6. Cross-link landing → docs (WhatsIncluded + FAQ)

    • Each WhatsIncluded card now click-throughs to the most relevant /docs/ page (12 new internal links from a high-PR landing page). Cards reveal a "Read the docs →" affordance on hover.
    • Section footer adds "Browse the full building-blocks reference →" CTA pointing at /docs/building-blocks/.
    • Each FAQ answer gets an optional link object rendered as a "Read the X →" link below the prose. Adds 9 more internal links to deeper docs (testing, architecture, modules, deployment, changelog, cross-cutting-concerns).

Build green; 18 pages built in 2.93s. Verified: docs pages emit 3 application/ld+json blocks (site graph + BreadcrumbList + TechArticle), homepage emits site graph + FAQPage, sitemap carries priority + changefreq per URL, /llms-full.txt is 429 lines covering all 16 docs pages.

  • feat(docs): nav Docs link points straight to introduction

Header / Footer / Breadcrumbs all link the "Docs" entry directly at /docs/getting-started/introduction/ (no redirect hop). Header keeps the previous active-state semantics via a new match field on the nav item — Docs still highlights for any /docs/* path even though its link target is deeper.

  • feat(docs): section index pages with auto-generated card grids

Each category now has a real index page that lists its child pages as a card grid (2-col / 1-col mobile). Cards show a monospace 01/02/… index, the page title, its description, and a chevron that nudges on hover. Empty-state placeholder card renders for sections that don't have child pages yet.

  • New SectionIndex.astro: discovers pages in src/content/docs/{section}, sorts by sidebar.order, excludes the index itself + hidden pages.
  • Registered as a global MDX component (no per-file imports needed).
  • Re-created getting-started/index.mdx + dropped the /docs/getting-started/ redirect so the new index is reachable.
  • Updated 10 other section index pages to use SectionIndex. contributing + changelog kept as single-page sections.
  • feat(docs): track-1 content — comparison pages + getting-started SEO

The first content writing pass. Adds three full comparison pages that rank for "X alternative" queries, wires them into the sidebar under a new Compare section, and adds per-page SEO frontmatter to the four Getting Started pages.

Comparison pages (~1100-1400 words each, fact-checked against repos)

  • /docs/compare/fsh-vs-abp/ — biggest direct competitor. Honest side-by-side (license, code ownership, DI, mediator, multitenancy stack, stars). "When to choose ABP / when to choose fullstackhero" with fair trade-offs. Migration notes (modules map, repositories become DbContexts, app services become Mediator handlers).
  • /docs/compare/fsh-vs-clean-architecture/ — Jason Taylor (~20.1k★) and Ardalis (~18.2k★) templates. Frames the difference as "they ship an architecture; fullstackhero ships ten production modules on top of an architecture." Acknowledges where CA templates are the better classroom + the better fit for small focused services.
  • /docs/compare/fsh-vs-blazorplate/ — paid closed-source ($499-$999) vs free open-source MIT. React vs Blazor, webhooks + OpenTelemetry deltas, hybrid-use guidance.

Each comparison page ships honest disclaimers ("this page is maintained by us, open an issue if anything is unfair") and links to the competitor's canonical site/repo. All Q→A claims are checked against either upstream repos or our Directory.Packages.props.

Compare section

  • New /docs/compare/index.mdx — section landing with a quick decision tree pointing the reader at the right comparison.
  • _sections.ts gains a 'compare' entry at order 12, between Deployment and Contributing — sidebar wiring automatic via the section list.
  • astro.config.mjs sitemap rule adds /docs/compare/* → priority 0.8 / monthly. These are high-conversion + rank for head-intent "X alternative" queries; deserve the bump.

Getting Started SEO frontmatter

  • Each of the four existing getting-started pages (introduction, prerequisites, install, quick-start) gains a seo block with keyword-targeted title, description, and keywords. The on-page H1 stays editorial; the <title> tag and meta description target long-tail searches like "fsh CLI", ".NET Aspire workload", "FullStackHero.NET.StarterKit dotnet new template".

Verified

  • Build green; 24 pages built in 3.17s (up from 18 last pass).
  • Sitemap carries all four compare URLs at priority 0.8.
  • llms-full.txt picked up the new content automatically (no code change needed — the static endpoint enumerates the docs collection).
  • FSH CLI claims verified: FullStackHero.CLI package + fsh tool command in src/Tools/CLI/FSH.CLI.csproj, fsh new + fsh doctor commands in Commands/.
  • feat(docs): /docs/ overview hub with chapter-plate category cards

/docs/ is now a real overview page that lists every category as a card grid. Each card carries a lucide icon, a huge ghosted watermark numeral (editorial accent), title, description, live page count, and an orchestrated hover sequence (lift + border + icon tint + watermark glow + arrow nudge on a single 320ms curve).

  • CategoryIndex.astro: discovers sections from _sections.ts, pulls descriptions from each section's index.mdx, counts child pages.
  • Registered as a global MDX component.
  • docs/index.mdx (new) renders the hub.
  • Header / Footer / Breadcrumbs re-point the "Docs" entry at /docs/.
  • Typography: body-font title, sentence-case page-count meta (no more mono-caps eyebrow / slug).
  • fix(docs): wire .not-prose utility so card grids escape prose styles

The card grids inside MDX pages were inheriting prose-context styles — the star li::before marker (rendering as little green +/sparkle marks around each card) and the :where(a) underline. The .not-prose class on the grids was a no-op because there was no actual rule wiring it.

Added a real .not-prose rule in globals.css that resets list-marker, list padding, and anchor text-decoration for any descendant subtree marked not-prose. SectionIndex picks up a belt-and-braces inline text-decoration:none to defeat any stray :where(a) cascade.

  • docs(modules): 10 module deep-dive pages — full first drafts

One MDX per module under /docs/modules/. All claims fact-checked against the runtime source — every endpoint table, version pin, domain-model excerpt, and configuration block was verified before landing in prose. Each page targets a specific long-tail search phrase via its seo.title and runs 1,000-1,800 words.

The ten pages

  • identity.mdx (order 1) — JWT + refresh + permissions + groups +
    impersonation + 2FA + sessions + password policy. ~1,800 words.
    48 endpoints tabulated. References IdentityModule.cs + Domain/
    FshUser.cs + Contracts/v1/Tokens/.
  • multitenancy.mdx (order 2) — Finbuckle strategy chain + cache store +
    EFCoreStore + per-tenant connection strings + provisioning state
    machine + tenant themes. ~1,400 words. 9 endpoints. Documents the
    root-operator header override + claim-strategy-pre-auth gotcha.
  • auditing.mdx (order 3) — SaveChanges interceptor + HTTP
    middleware + fluent Audit builder + JSON masking + channel
    publisher + SQL/DLQ sinks + retention job. ~1,500 words. All
    seven read endpoints + the AuditHttpOptions / AuditRetentionOptions
    configuration blocks documented verbatim.
  • files.mdx (order 4) — presigned URL flow + per-OwnerType
    IFileAccessPolicy + finalize state machine + Files Categories
    config + IFileScanner hook + orphan/retention purge jobs +
    FileFinalizedIntegrationEvent. ~1,300 words.
  • chat.mdx (order 5) — three channel kinds + idempotent
    DirectKey for DMs + tombstone soft-delete + threads + reactions
    • pinning + mention parser + AppHub realtime + Redis backplane
    • 3s typing throttle. ~1,500 words. 22 endpoints. Documents the
      module-order quirk (Notifications 750 before Chat 800).
  • notifications.mdx (order 6) — denormalized inbox row + integration
    event bridge + SignalR push to user:{id} + idempotent mark-read +
    bulk ExecuteUpdateAsync mark-all-read. ~1,000 words. 4 endpoints.
    Includes step-by-step "add a new notification type from another
    module" extension recipe.
  • webhooks.mdx (order 7) — tenant-scoped subscriptions + open-
    generic WebhookFanoutHandler + HMAC-SHA256 signing +
    Hangfire AutomaticRetry with 30s/2m/10m/1h backoff + delivery log.
    ~1,400 words. 5 endpoints. Documents the "restore tenant context
    manually" gotcha + ships a complete C# signature-verification
    recipe for subscribers.
  • billing.mdx (order 8) — already shipped in prior commit
  • catalog.mdx (order 9) — already shipped in prior commit
  • tickets.mdx (order 10) — already shipped in prior commit

Across all ten pages we now document

  • 234 endpoints (verified from MapEndpoint extensions)
  • 30+ aggregate / entity types (with sealed-class + invariant excerpts)
  • 100+ commands / queries from the Contracts assemblies
  • Every IOptions config block with appsettings keys
  • Every Hangfire recurring job by cron expression
  • Every IGlobalEntity / tenant-context quirk worth knowing

Build green; 34 pages built in 4.75s (up from 27 last pass).

  • docs(building-blocks): 12 building-block reference pages

One MDX per FSH.Framework block under /docs/building-blocks/. Each page documents the block's public extension methods, interfaces, base types, options, and consuming-module examples — fact-checked against the BuildingBlocks/ source.

The twelve pages

  • core.mdx (1) BaseEntity, AggregateRoot, DomainEvent,
    ICurrentUser, CustomException hierarchy
  • persistence.mdx (2) BaseDbContext + auto-applied filters,
    Specification, audit + domain-event
    interceptors
  • shared.mdx (3) AppTenantInfo, PermissionConstants,
    FshPermission, claim/action/resource
    constants, audit attributes
  • web.mdx (4) AddHeroPlatform / UseHeroPlatform,
    ModuleLoader, GlobalExceptionHandler,
    ValidationBehavior, CurrentUserMiddleware
  • caching.mdx (5) HybridCache + ObservableHybridCache
    decorator with OTel; stampede protection
  • eventing-abstractions.mdx (6) IIntegrationEvent, IIntegrationEventHandler,
    IEventBus, IEventSerializer (dep-free)
  • eventing.mdx (7) InMemoryEventBus, RabbitMqEventBus,
    EfCoreOutboxStore, EfCoreInboxStore,
    OutboxDispatcher
  • jobs.mdx (8) Hangfire wiring + FshJobActivator +
    FshJobFilter (tenant context) + dashboard
    basic auth + stale-lock cleanup
  • mailing.mdx (9) IMailService + SMTP (MailKit) and
    SendGrid implementations
  • storage.mdx (10) IStorageService + LocalStorageService
    and S3StorageService + presigned URLs
    + QuotaMeteredStorageService decorator
  • quota.mdx (11) IQuotaService (Redis/InMemory/Noop) +
    plan resolution + gauge providers +
    enforcement middleware
  • blazor-ui.mdx (12) Placeholder — v1 ships React frontends;
    block reserved for future Blazor lib

Per page: ~700-1,200 words covering purpose, public surface, options, how modules consume it, extensibility recipes, and gotchas. Every extension method, interface, and option type listed has a file path in the appendix.

Cross-link density between blocks and modules is dense — Storage links to Files, Quota links to Identity's user gauge + Billing's plan keys, Eventing links to Notifications + Webhooks, etc.

Build green; 46 pages built in 5.73s (up from 34).

  • docs(architecture): 4 architecture pillar pages

Synthesizes patterns documented across the module and building-block pages into four conceptual deep-dives at /docs/architecture/.

  • modular-monolith.mdx (order 2) — the outer shape: ten modules
    with one-way Contracts boundaries enforced by NetArchTest, load
    order, three patterns for cross-module talk (service interfaces,
    domain events, integration events), and the extract-a-service
    migration path when scaling demands it. ~1,400 words.
  • vertical-slice.mdx (order 3) — the inner shape: one feature
    per folder with endpoint + command + handler + validator + tests.
    Complete RegisterUser end-to-end excerpt. Conventions (sealed,
    ValueTask, ConfigureAwait(false), one endpoint per slice).
    Trade-offs vs Clean Architecture. ~1,200 words.
  • multitenancy-deep-dive.mdx (order 4) — tenancy as default. Three
    layers (HTTP/data/jobs), Finbuckle strategy chain + claim-pre-auth
    gotcha, BaseDbContext.ApplyTenantIsolationByDefault(), IGlobalEntity
    opt-out, named SoftDelete filter, cross-tenant query pattern
    (IgnoreQueryFilters + explicit re-filter), per-tenant connection
    strings, tag-based cache eviction. ~1,400 words.
  • dependency-injection.mdx (order 5) — composition root: 4-line
    Program.cs, the IModule contract, Mediator 3 source-gen MSG0007
    gotcha, FluentValidation auto-registration, three lifetimes, the
    four wire points for a new module (with the Mediator-list silent-
    failure flag), middleware pipeline order including CORS-before-
    HTTPS-redirect and CurrentUserMiddleware-last rules. ~1,300 words.

Build green; 50 pages built in 6.11s (up from 46).

  • feat(docs): section cards match chapter-plate aesthetic + fix not-prose

SectionIndex cards now use the same modern design language as the CategoryIndex hub: huge ghosted watermark numeral as the editorial accent, body-font title, line-clamped description, pagination eyebrow (01 / 04), and a unified hover sequence on a single 320ms curve.

Also fixes the .not-prose escape hatch. Previous selectors required .not-prose to be an ancestor of the

    , but the class lives ON the
      itself — so prose's star li::before marker kept leaking into both card grids. Rewrote with :where(.not-prose) selectors that match either placement; !important plus :where()'s 0-specificity cleanly defeats the prose cascade.

      • docs(sections): testing + security + cross-cutting-concerns full pages

      Replaces three stub section indexes with substantial fact-checked content. Each page now stands as a complete reference; the wiring is removed since these are no longer just landing pages for child docs.

      testing/index.mdx (~1,800 words)

      • Three-layer model: unit (xUnit + Shouldly + NSubstitute + AutoFixture), integration (WebApplicationFactory + Testcontainers Postgres/Redis/ MinIO), architecture (NetArchTest).
      • The test stack pinned in Directory.Packages.props, the test project layout under src/Tests/, the naming convention "Method_Should_X_When_Y".
      • Real RegisterUserCommandHandlerTests + ProductsEndpointTests excerpts as templates.
      • All 9 NetArchTest rule families enumerated (ModuleBoundary, ContractsPurity, BuildingBlocksIndependence, CircularReference, HandlerValidatorPairing, DomainEntity, ApiVersioning, FeatureFolder, HostArchitecture). 48 architecture tests total.
      • "How to add a new test" recipes for each layer.
      • Honest disclaimers: no load tests, no visual regression, no chaos.

      security/index.mdx (~2,000 words)

      • The eight pillars: authentication, authorization, tenant isolation, audit, rate limiting, webhook integrity, secret protection, data masking.
      • JWT bearer + rotating refresh, 5-attempt lockout, password policy (12-char min, 5-history, 90-day expiry), email confirmation, optional 2FA TOTP.
      • Permission-based authorization via .RequirePermission(); roles as groupings, permissions as the gate; the RequiredPermissionAttribute silent-no-op gotcha flagged inline.
      • Tenant isolation default; IgnoreQueryFilters discipline; named SoftDelete filter; IGlobalEntity opt-out.
      • Impersonation grant lifecycle + revocation list.
      • Rate-limit auth policy on 6 endpoints enumerated.
      • HMAC-SHA256 webhook signing + full subscriber-side verifier excerpt.
      • Redis-backed Data Protection key persistence.
      • Audit + JSON masking + retention.
      • CORS-before-HTTPS-redirect + SignalR credentialed CORS gotcha.
      • A 10-item day-one production security checklist.

      cross-cutting-concerns/index.mdx (~1,800 words)

      • The 11 platform features in a single table + per-feature deep dives.
      • HybridCache stampede protection + tag-based eviction + shared multiplexer rationale.
      • Hangfire FshJobFilter (tenant context), FshJobActivator (scoped DI), HangfireTelemetryFilter, plus the 5 recurring jobs the kit ships.
      • OpenTelemetry: traces (incl. MediatorTracingBehavior), metrics, logs over OTLP; Serilog enrichers.
      • Idempotency-Key replay via HybridCache.
      • Feature flags with TenantFeatureFilter for per-tenant overrides.
      • Rate limiting (vs Quota — different concerns).
      • Health checks: db, redis, MinIO, tenant migrations.
      • SSE + SignalR + Redis backplane + AppHub group conventions.
      • HTTP resilience pipeline (Polly v8 via Microsoft.Extensions.Http.Resilience).
      • ProblemDetails (RFC 9457) global handler + Mediator pipeline behaviors (ValidationBehavior, MediatorTracingBehavior).

      All claims fact-checked against the source — versions pinned from Directory.Packages.props, recurring-job crons verified from each module's WebhooksModule.cs / BillingModule.cs / FilesModule.cs / etc., NetArchTest rule families verified from src/Tests/Architecture.Tests/.

      Build green; 50 pages (unchanged — these replaced existing stubs).

      • docs(cross-cutting): split into 11 per-topic pages

      Replaces the single-page cross-cutting-concerns index with one MDX per concern under /docs/cross-cutting-concerns/. Each page is now rank-able for its own long-tail search query and the section index auto-lists children via .

      The eleven new pages

      • caching.mdx (1) HybridCache L1+L2 + stampede + tag eviction
        + shared multiplexer
      • background-jobs.mdx (2) Hangfire + FshJobFilter (tenant context) +
        FshJobActivator (scoped DI) + the 5 cron
        jobs the kit ships + retry attributes
      • observability.mdx (3) Serilog enrichers + OpenTelemetry 1.15 +
        instrumentation matrix + MediatorTracing
      • idempotency.mdx (4) Idempotency-Key header + HybridCache replay
        + retention window + content-dedupe disclaimer
      • feature-flags.mdx (5) Microsoft.FeatureManagement + TenantFeatureFilter
        + staged rollout / kill switch / per-env patterns
      • rate-limiting.mdx (6) auth policy on 6 endpoints + AuthRateLimitWiringTests
        + adding custom policies + monitoring 429s
      • health-checks.mdx (7) /health and /health/ready + per-DbContext +
        Redis + MinIO + TenantMigrationsHealthCheck
        + k8s liveness/readiness probe wiring
      • server-sent-events.mdx (8) SSE primitive + browser EventSource client +
        ?access_token= auth + heartbeat config
      • realtime.mdx (9) SignalR over Redis backplane + AppHub group
        conventions (user:{id} + channel:{id}) +
        Context.User vs ICurrentUser gotcha +
        TestServer-no-WebSocket integration testing
      • http-resilience.mdx (10) Polly v8 standard pipeline + HTTP-layer vs
        job-layer retry split + circuit breaker +
        hedging + non-idempotent retry pitfall
      • error-handling.mdx (11) GlobalExceptionHandler + RFC 9457
        ProblemDetails + the 4-exception hierarchy +
        FluentValidation field errors + throw-from-
        domain idiom

      Index page is now slim — short intro + auto-listing.

      Build green; 61 pages built in 8.19s (up from 50).

      • docs(security): split into 8 per-topic pages

      Replaces the single-page security index with one MDX per concern.

      The eight new pages

      • authentication.mdx (1) JWT bearer + rotating refresh + lockout +
        password policy + email confirmation flow
      • authorization.mdx (2) Permission-based gates, role+group aggregation,
        and the silent-no-op gotcha for duplicate
        RequiredPermissionAttribute declarations
      • impersonation.mdx (3) Time-bound grants, IGlobalEntity persistence,
        jti revocation list, cross-tenant root override,
        full audit trail
      • two-factor.mdx (4) TOTP enrol/verify/disable; recovery-codes
        gap flagged as recommended add-on
      • webhook-signing.mdx (5) HMAC-SHA256 signing scheme + both C# and
        Node/Express subscriber-side verification
        recipes + replay protection via delivery id
      • data-protection.mdx (6) Redis-backed key persistence so cookies +
        antiforgery + email tokens survive rolling deploys
      • cors-and-headers.mdx (7) CORS-before-HTTPS-redirect ordering, the
        SignalR-credentialed-CORS gotcha, full CSP +
        HSTS + X-Frame-Options reference
      • production-checklist.mdx (8) The 10-item release gate — JWT rotation,
        password policy, CORS, headers, rate limits,
        HTTPS, debug-endpoint lockdown, Hangfire auth,
        Data Protection persistence, audit retention

      Index page is now a slim landing with auto-listing.

      Build green; 69 pages built in 7.27s (up from 61).

      • docs(testing): split into 6 per-topic pages

      Replaces the single-page testing index with one MDX per layer plus meta pages (fixtures, running, writing).

      The six new pages

      • unit-tests.mdx (1) xUnit + Shouldly + NSubstitute +
        AutoFixture; Method_Should_X_When_Y naming;
        three ground rules (Shouldly, direct SUT,
        no infrastructure); aggregate-invariant +
        validator + mock-verification patterns
      • integration-tests.mdx (2) WebApplicationFactory + Testcontainers
        Postgres/Redis/MinIO; FshWebApplicationFactory
        + Seed.* + CreateAuthenticatedClient helpers;
        full ImpersonationTests example
      • architecture-tests.mdx (3) 9 rule families + 48 tests; module-boundary
        + endpoint-convention + feature-folder
        examples; "specification in XML doc + assertion
        in code" pattern; what arch tests don't cover
      • fixtures-and-seed-data.mdx (4) DatabaseFixture + RedisFixture + MinioFixture
        composition; FshWebApplicationFactory wiring;
        Seed.* static accessor; insert-don't-mutate
        discipline; common mistakes
      • running-tests.mdx (5) Commands (everything, single project, filter,
        verbose, coverage); Docker requirement; full
        GitHub Actions workflow; CI sharding pattern;
        container cleanup
      • writing-new-tests.mdx (6) Three step-by-step recipes: unit (with full
        handler example), integration (with full
        endpoint example), architecture rule (with
        IL-walker disclaimer)

      Index page is now a slim landing with .

      Build green; 75 pages built in 8.60s (up from 69).

      • docs(frontend + guides): impersonation walkthrough + frontend split + screenshot placeholders

      Adds a comprehensive operator impersonation guide and splits the frontend section into per-app pages. New MDX component renders styled placeholder boxes when the image asset doesn't exist yet — owner can drop screenshots into docs/public/screenshots/ and the placeholders flip to real images automatically.

      New MDX component

      • components/docs/Screenshot.astro — file-exists check at build time; styled dashed-border placeholder when missing, real + caption when present. Wired into mdxComponents export.

      New guides page

      • guides/operator-impersonation.mdx (~1,700 words) — end-to-end UI walkthrough: open admin → find user → start grant (with mandatory reason) → act as user in new tab → end or revoke → audit trail query. 9 screenshot placeholders covering every step. Plus a cross-tenant section explaining the root-operator override, audit query examples (Identity.Impersonation events), operational policies (mandate reason, short duration, alert on patterns, train operators), and 4 common-question answers (notified? chained? self? lost device?).

      Frontend section — 5 sub-pages + slim index

      • admin.mdx (~1,500 words) — operator-facing app: 11 page
        areas, JWT auth flow with refresh, permission-gated UI, TanStack
        Query patterns, SignalR notifications, the impersonation modal +
        flow + audit, theme editor for tenants. 8 screenshot placeholders.
      • dashboard.mdx (~1,400 words) — tenant-facing app: catalog,
        chat (the densest realtime page), files presigned upload, tickets,
        identity (profile + sessions + 2FA). 12 screenshot placeholders
        covering every major page.
      • architecture.mdx (~1,500 words) — shared patterns across both
        apps: folder layout, provider stack order, apiClient interceptors
        (auth + refresh + ProblemDetails), TanStack Query conventions,
        RouteGuard, RealtimeProvider, env handling.
      • theming.mdx (~1,200 words) — per-tenant theming via CSS
        custom properties, the 9 palette roles (light + dark), brand assets,
        typography, layout. Theme editor UI walkthrough with 4 screenshot
        placeholders. Loading-flash mitigation pattern + dark mode story.
      • development.mdx (~1,400 words) — Vite npm scripts, Aspire-
        orchestrated vs npm-direct dev, production build, runtime config via
        envsubst (full Dockerfile + entrypoint.sh), Nginx SPA config,
        Vercel / Netlify / S3+CloudFront / Cloudflare Pages deployment shapes,
        Vitest setup, common issues.

      Total: ~33 screenshot placeholders embedded across the new pages. Each Screenshot has a target path under public/screenshots/ — drop images there and they replace the placeholders automatically.

      Build green; 81 pages built in 9.40s (up from 75).

      • fix(docs): hide screenshot placeholders in production builds

      The placeholder box now renders only in dev (`astro dev`) where the author needs to see what's still missing. Production builds (`astro build`) emit nothing when the asset doesn't exist — so missing screenshots leave no trace in shipped docs.

      The "image exists" path is unchanged; once a screenshot lands at the expected path under docs/public/screenshots/, the real renders in any environment.

      Toggle uses import.meta.env.DEV, which Astro/Vite sets to true under the dev server and false during production builds.

      Verified: 0 .screenshot-placeholder elements in the production build across operator-impersonation, admin, dashboard, theming pages.

      • feat(dashboard+backend): v1 UI polish, file visibility/sharing, permission catalog

      Dashboard: revert warm-paper-hue chassis to untinted neutrals; polish list primitives (combobox, density-toggle, empty-state, hero, pagination, sort, stat) and add entity-detail/entity-shell/tone-icon-tile; tighten auth shell, command palette, file pickers, notifications, sidebar/topbar, theme toggle; page-level passes across activity, audits, auth, catalog, chat, files, login, settings, system, tickets.

      Files: add ChangeFileVisibility command + ListSharedFiles query, surface visibility on FileAssetDto.

      Identity: add GetPermissionCatalog query/endpoint; RoleService surfaces catalog data.

      Tests: add PermissionCatalogTests; add adminPassword to TenantCreation/TenantActivation payloads; update RoleManagementTests.

      • fix(tests): update role helpers to deserialize PagedResponse

      GET /roles now returns PagedResponse (paged) instead of a flat RoleDto[]; three integration test helpers still expected the array shape, blowing up on the first byte of every response and failing 8 tests across the Roles and Users suites.

      • feat(dashboard): A+ rollup — deps + a11y + perf + tokens + UX

      Closes the P0 + most P1 items from the 2026-05-21 dashboard audit (docs/superpowers/audits/2026-05-21-dashboard-world-class-audit.md).

      Bundle: main JS 197.5 KB -> 178.0 KB gzip (-10%). New lazy chunks: command-palette-dialog 8.2 KB, SignalR 14.5 KB. Chat-specific CSS extracted to a 3.9 KB per-route chunk. Main CSS 127.5 -> 121.4 KB.

      Build + lint + Playwright (37/37) all green. Zero npm advisories.

      Dependency hygiene

      • Wave-1 bumps: react 19.2.6, react-dom 19.2.6, @tanstack/react-query 5.100.11, @tanstack/react-virtual 3.13.25, react-router-dom 7.15.1, tailwindcss 4.3.0, @tailwindcss/vite 4.3.0, tailwind-merge 3.6, vite 7.3.3, @vitejs/plugin-react 4.7, typescript 5.9.3, typescript-eslint 8.59.4, eslint 9.39.4, plus @types/react, @types/react-dom, @types/node patches.
      • Removed unused deps: recharts (declared, zero imports), react-hook-form, @hookform/resolvers, zod, @types/lodash, autoprefixer (Tailwind v4 handles prefixing natively).

      Dead code

      • Deleted 10 orphan files (zero cross-grep consumers): file-gallery, sse/live-feed, sse/sse-status-badge, theme-toggle, list/density-toggle, list/empty-state, list/list-hero, list/pagination, list/sort-chips, list/stat. components/sse/ directory removed.
      • Pruned components/list/index.ts barrel. Removed unused API exports: getAuditsByTrace, reorderProductImages, discoverChannels, restoreChannel, getRoleById. Dropped unused export keywords (BRAND_LADDER).

      Tokens + visual cohesion

      • Dropped backdrop-grayscale from the Dialog scrim (it was desaturating the entire page behind every modal).
      • Light-mode elevation restored: --surface-3 now differs from --card / --background so cards float again.
      • Muted-foreground bumped (dark L 0.680 -> 0.730; light 0.575 -> 0.500) so placeholders + chevrons clear WCAG 2.2 AA 4.5:1.
      • Added canonical type scale: --text-display-page/section/card/stat (Tailwind v4 auto-generates the text-display-* utilities).
      • Defined missing fsh-sheet-in/out keyframes for the mobile drawer (previously referenced but undefined).
      • Replaced 19 shadow-[0_1px_2px_oklch(...0.04)] literals across 12 files with the shadow-xs token. Fixed clipped oklch lightness in overview hover-borders (now uses --color-border-strong).
      • Removed dead .chat-empty-hero rule.

      Type scale migration

      • Migrated h1s in EntityPageHeader, PageHero, OverviewPage, NotFoundPage to the new tokens. Chat channel header demoted h1 -> h2 so each route has a single h1.

      Fonts

      • index.html eager Google Fonts trimmed from 12 families to 3 (Figtree + Outfit + JetBrains Mono). The other 9 are lazy-injected by ensureLazyFontsLoaded() when Appearance settings mounts, or synchronously at boot when the user has a non-default body font stored in localStorage. ~200-400 KB cold-load savings.

      Accessibility (WCAG 2.2 AA)

      • Programmatic-label fixes: aria-label on cmdk Command.Input + chat composer textarea; aria-expanded={!collapsed} on sidebar collapse button.
      • FilePreviewDialog no longer renders a whitespace-only DialogDescription (SR-read as empty description).
      • Field primitive: required indicator gets an sr-only "required" sibling alongside the visual dot.
      • RouteError stack trace gated behind import.meta.env.DEV — prod users see a recoverable card with no internals exposed.
      • RealtimeStatusPill: new announce prop, single mount (bell footer) now owns the live region — the chat-rail instance stops duplicate-announcing reconnect events.
      • role="log" aria-live="polite" on chat MessageList scroll container + Activity feed (mobile cards + desktop table).
      • role="status" aria-live="polite" on typing-indicator.
      • Touch targets bumped from h-5 w-5 (20px) to h-6 w-6 (24px) on composer reply-quote/attachment/upload clears, channel-rail filter clear + section-action plus, login eye-toggle, with focus-visible rings added where missing.

      Forms

      • aria-invalid + aria-describedby wired on login + forgot-password
        • reset-password forms so SR users hear errors linked to the fields they apply to. Sibling error blocks got stable ids.

      Performance

      • @microsoft/signalr is now dynamic-imported inside realtime-context's connect flow (37 KB gzip off the main shell). HubConnectionState comparisons rewritten as string-literal so the shutdown / invoke paths don't pull SignalR back in.
      • CommandPaletteDialog extracted to its own file and React.lazy imported by CommandPaletteRoot — cmdk + the full action graph load on first Cmd+K, not on cold start.
      • SseContext split into useSseStatus (status + eventCount) and useSseEvents (events array). Topbar SSE dot + bell pill stop re-rendering on every SSE event; only LiveFeed / Activity / Overview's livefeed-body subscribe to the events stream.
      • Avatar gained loading="lazy" + decoding="async" + explicit width/height (matches sizeClass), eliminating CLS during avatar swap-in.

      UX

      • Command palette expanded: Identity (users/roles/groups), Catalog (products/brands/categories), Tickets, Files, Chat, Trash, Sessions, Appearance — every leaf route is now navigable from Cmd+K. New Create group with 9 entries (user/role/group/product/ brand/category/ticket/channel/file-upload).
      • Tickets list + detail render assignee/reporter via useUserDisplay (real names + avatars) instead of authorUserId.slice(0,8) + "...".
      • Chat-specific CSS rules (typing dots, unread divider, day rule, reaction chip, jump pill, mention pill) moved to pages/chat/chat.css — Vite extracts them into the chat-page CSS chunk so other pages stop shipping ~3 KB of selectors they don't use. .chat-status-pill stays in globals.css because the global notification bell consumes it.

      ESLint

      • jsx-a11y/no-noninteractive-element-interactions promoted from warn -> error (configured to exclude onError so legitimate image fallback handlers don't trip). Added no-noninteractive-element- to-interactive-role / no-aria-hidden-on-focusable / anchor-has- content as error. Fixed the one violation in user-picker.tsx (ul role=listbox -> div role=listbox).

      Tests

      • Refreshed two Playwright auth specs (forgot-password, reset-password) that were asserting the old "// 02.FORGOT- PASSWORD" / "// 03.RESET-PASSWORD" eyebrow chrome and chip rendering that the v1 AuthShell redesign (commit 519d664d) retired. Tests now target the current headline + body text.

      Docs

      • Added docs/superpowers/audits/2026-05-21-dashboard-world-class- audit.md (the original six-agent composite audit, updated with a Status preamble noting what landed in this rollup).
      • feat(docs): page view counter (D1) + frontmatter dates + hero copy

      Add a Cloudflare D1-backed page view counter to the docs site:

      • worker.ts handles POST/GET /api/views with KV per-(IP+UA) dedup (1h TTL) and delegates all non-API paths to the static assets binding
      • migrations/0001_init.sql defines the views table (+ count index)
      • client beacon (views-counter.ts) posts the slug and renders the count into a PageHeader chip with an eased count-up reveal; dev shows a sample, prod stays hidden until the beacon resolves
      • wrangler.toml wires the worker entry, assets, D1, and KV bindings

      Move page dates into MDX frontmatter (lastUpdated), backfilled across all 79 docs from git history, and remove the broken remark-modified-time plugin (Astro 6's glob loader discards remark frontmatter mutations, so the value never reached entry.data).

      Reword the hero headline to "built to ship, not to demo."

      • refactor(identity,multitenancy): propagate CancellationToken through service layer + endpoints

      Full-sweep CancellationToken audit (Roslyn navigator AP009). Adds `CancellationToken cancellatio…

* build(migrator): chiseled Dockerfile + APP_UID + csproj container hardening

* build(deploy): docker-compose .env.example with all knobs documented

* feat(docs): redesign landing — conversion-focused with modern code window

Replaces the placeholder hero/feature-grid with a six-section landing
designed for developer conversion:

- Hero: editorial display headline with gradient-text inflection, single
  strong subhead, dual CTA, immediate install command (copyable) below
  the fold. Brand-shadow blur + subtle dot-field for atmosphere.
- CodeFirst: split section with editorial copy left, custom-framed code
  window right showing real RegisterUser feature folder (endpoint /
  handler / validator tabs). Mac-style traffic lights, green-accent
  active tab underline, hover gradient border, status footer.
- WhatsIncluded: 12-item refined checklist (not boxy cards). Scannable.
- ModuleShowcase: 3 deep cards (Identity / Multitenancy / Auditing) —
  tagline, capability bullets, real code snippet, deep-dive link each.
  Retains magnetic-shimmer hover from blog token system.
- TechStack: 12-tech compact grid with vertical border accent on hover.
- FinalCta: editorial close with repeated install command + CTAs +
  '≈5 minutes to localhost' time-to-running indicator.

Codeblocks use Expressive Code's <Code /> programmatic component with
frame=none so the custom window chrome (traffic lights, tabs, status
bar) wraps clean EC output. EC config externalized to ec.config.mjs as
required by the programmatic <Code /> path.

Every color/surface uses semantic tokens — no hardcoded colors except
mac traffic light hexes (intentional, vendor-recognizable signal).
Cleanly inverts between light and dark.

* build(deploy): postgres init SQL — required extensions

* build(deploy): production docker-compose.yml — full stack on one host

* docs(deploy): five-minute docker-compose deploy guide

* fix(docs): theme toggle, nav active, alignment, code panel polish

Batch of bug fixes from initial review pass:

- ThemeToggle: switched script to is:inline plain JS (was hoisted module
  with TS annotations). Dropped View Transitions wrapper — was the
  silent failure mode. Listener now attaches deterministically.
- Header nav active state: previous startsWith() matched /docs/ for any
  /docs/* path, lighting up both 'Docs' and the actual section. New
  logic picks the longest matching prefix as the single active item.
- Container width: header + footer were max-w-6xl while docs grid was
  max-w-7xl, causing visible left/right misalignment on docs pages.
  Unified all surfaces to max-w-7xl.
- prose.css ul bullets: referenced ../icons/svgs/star.svg from the blog
  that we never copied → runtime 404. Replaced with a green dot using
  --primary token (consistent with our design).
- Dropped @astrojs/react integration: zero React components remain
  after ThemeToggle was converted to Astro. Eliminated the Vite 7
  rolldown 'Missing field moduleType' dev errors from react-refresh.
- Code panel rewrite (code-window.css): now targets the real DOM
  (.expressive-code .frame, not the outer div). Soft elevation, brand
  hover border, language badge floats above pre on hover, copy button
  positioned cleanly inside header. Mac traffic lights (already from
  EC) sit naturally in the gradient title bar.

* fix(docs): use blog's exact MDX codeblock style (drop custom code-window.css)

The blog's MDX codeblock treatment lives in prose.css and is minimal:
rounded-xl pre, near-invisible white-with-low-opacity border, brand-tinted
border on hover, copy button scales 1.05 with brand-tinted bg on hover,
plus a gradient line-highlight indicator. My custom code-window.css was
overdesigning a problem the blog had already solved.

- Deleted docs/src/styles/code-window.css
- Removed its import from globals.css
- prose.css already inherited from the blog verbatim; updated the two
  blog-purple references: rgb(118 89 236, ...) → rgb(22 163 74, ...)
  (#16a34a, our --primary-soft mid-green) and #4bf3c8 → #4ade80 (our
  dark-mode --primary-soft) in the line-highlight gradient.

* fix(deploy): resolve 3 issues found during e2e smoke test

- docker-compose migrator: add --seed flag so IdentityDbInitializer
  runs SeedAdminUserAsync and admin@root.com is seeded on first boot
- docker-compose api: add AllowedHosts=* env var to override the
  appsettings.Production.json restriction (was api.example.com,
  which Kestrel rejected for localhost requests)
- docker-compose api: add HangfireOptions__UserName/Password env vars
  (required by ValidateOnStart() in Jobs/Extensions.cs); document in
  .env.example with generation instructions
- appsettings.Production.json: set AllowedHosts to * (host filtering
  belongs at the reverse proxy layer, not Kestrel)

All surfaces verified: health/live, health/ready (all 14 checks
Healthy), admin config.json, dashboard config.json, both 200 OK,
admin@root.com login returns JWT, profile returns correct email.
Volume durability: migrator re-runs idempotently on second boot.

* test(deploy): end-to-end docker-compose smoke verified locally

All 7 steps passed: stack up, migrator exit 0, health Healthy,
admin/dashboard config.json correct, login + profile verified,
volume durability confirmed, stack torn down and .env removed.

* docs: point README at deploy/docker for the production deploy story

* feat(docs): redesign CodeFirst as VS Code-style editor with file tree

Shows the full VSA feature-folder structure as a real-feeling IDE pane:

- Title bar: mac traffic lights, breadcrumb path (Modules.Identity /
  Features / v1 / Users / RegisterUser / <active>.cs), C# lang badge
- Left column (file tree): the Users/ folder with RegisterUser/
  expanded showing its three files, and five other collapsed feature
  folders (LoginUser, AssignUserRole, ChangePassword, DeleteUser,
  RefreshToken) — visually demonstrates that each feature is its own
  folder with its own files
- Active file: green left-border accent + tinted background + primary
  text color. Inactive: muted with hover row tint
- Right column: code panel with EC frame=none (chrome stripped, just
  the syntax-highlighted body)
- Status footer: project name, encoding, line count updates with selection
- Breadcrumb's filename and line count both swap on click
- Hover gradient ring behind the editor (mask-composite trick)
- Mobile: tree stacks above code with bottom border instead of right
- Indentation guide line (1px vertical) inside the RegisterUser folder
- Chevrons + folder icons via inline SVGs (lucide-shaped) — no astro-icon
  dep needed for the section

* docs(spec): editorial alignment of docs site with codewithmukesh/blog

Spec for the polish pass that ports the blog's brand/* primitive system
(Button, Card, Callout, Pill, Kbd) into the docs site, adds the eyebrow
numbering pattern, wires magnetic-cards + animated-IDE patterns, and
aligns ExpressiveCode theme. Green stays as the FSH brand color.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(container): use numeric UID 1654 in <ContainerUser>

MSBuild does not expand shell-style variables in <ContainerUser>, so
the literal string "$APP_UID" was written into the OCI image config's
User field by `dotnet publish /t:PublishContainer`. At `docker run`
time the daemon could not resolve that "user" and exited with
`unable to find user $APP_UID: no matching entries in passwd file`,
breaking the DbMigrator Container Smoke job in CI.

The hand-written Dockerfiles keep `USER $APP_UID` because Docker's
parser does expand ENV vars inside the USER directive at build time.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(docs): editorial alignment with codewithmukesh/blog

Ports the blog's brand primitive system (Button, Card, Callout, Pill,
Kbd) into the docs site and adopts the magazine-style numbered eyebrow
rhythm across the six landing sections. Green stays as the FSH brand
color; primitives flow through var(--primary) so the swap is automatic.

- brand/ primitives ported verbatim with var(--primary) substitutions
  in Card.astro (color-mix on hover glows/borders).
- New SectionEyebrow primitive: mono uppercase tabular-nums "01 · …"
  pattern with a primary-tinted accent rule.
- New AnimatedIde wrapper: pointer-tracked 3D tilt + cursor-following
  radial spotlight via --mouse-x / --mouse-y custom properties; wraps
  the CodeFirst editor. No-op on touch / reduced-motion.
- Magnetic-cards script ported into docs/src/scripts/ and wired from
  BaseLayout. Handles both [data-magnetic-card] and [data-magnetic-ide].
- ExpressiveCode config extracted into ec.config.mjs (function-valued
  options aren't JSON-serializable when inlined). Houston dark theme +
  green-tinted frame/marker accents.
- Hero CTAs + FinalCta CTAs + Header GitHub button now use the Button
  primitive (ink-on-paper primary). Header Ctrl·K hint uses <Kbd>.
- Hero gains a tabular-nums proof strip (".NET 10 · 3 modules · 598
  tests · 5 min to localhost") + mono "$ get running locally" prompt.
- Prose list marker swaps from a dot to the star SVG used by the blog.

Build green; 10 pages built in 2.13s.

* feat(docs): redesign Hero with animated terminal + stat cards

Replaces the install-and-CTA-focused hero with a magazine-style
conversion surface modeled on the dotnet-claude-kit hero from
codewithmukesh/blog.

What changed
- Magazine eyebrow rule across the top: "Open source · .NET 10 starter
  kit  ───  v1 · MIT · production-ready" (tabular-nums mono, primary
  on the left, muted on the right, hairline rule between).
- Display headline now splits font-light lead-in ("The .NET starter
  kit") from font-bold payoff ("that actually ships.") with a green
  gradient underline strip beneath the bold phrase.
- Subtitle bolds the three pillars (multitenancy, identity,
  observability) and lands on a feature-work-not-scaffolding payoff.
- Trust strip below the CTAs: v1 · MIT licensed · .NET 10 / C# 14 ·
  PostgreSQL · Redis · React.
- Animated terminal preview: typewriter for the dotnet run command,
  then staggered reveal of an Aspire orchestration boot sequence
  (PostgreSQL, Redis, API, Dashboard, Hangfire) ending in
  "Ready in 4.2s · 5 services orchestrated · multitenancy enforced".
  The blinking emerald cursor animates while the command types out.
- "What ships in v1" stat-card grid (4 metric cards): 3 modules · 12+
  building blocks · 598 tests · 5 min to localhost. Each card has a
  large tabular-nums display number with a tinted unit superscript, a
  hover corner glow, and an EQ-tick bar that "dances" on hover.

Motion and accessibility
- All animations gated on prefers-reduced-motion: the typewriter
  jumps to its full text immediately, line reveals fire without delay,
  and the cursor blink + tick dance are disabled.
- Terminal cursor + tick dance are CSS keyframe animations, GPU-
  composited via transform/opacity only.
- Script is idempotent (data-hero-init guard) so it survives
  astro:after-swap from Astro view transitions.

Build green; 10 pages built in 1.90s.

* feat(docs): header nav — add Home, drop Reference

Order is now Home · Docs · Modules · Recipes (down from 4 sections
that put Docs first and included a stubbed Reference link). The
liquid hover indicator and longest-prefix active-state logic both
work unchanged.

* feat(docs): wire FullStackHero logo + full favicon set

Header brand link now leads with the 512×512 FSH logo (rendered at
28×28, rounded corners) ahead of the lowercase "fullstackhero" mark
and the "/docs" mono suffix.

BaseLayout serves the full favicon ladder so every browser and OS gets
a sensible icon:
  - favicon.ico (broad fallback, 48×48 multi-res)
  - favicon.svg (modern vector)
  - favicon-32x32.png + favicon-16x16.png (older PNG ladder)
  - logo-fullstackhero.png as the apple-touch-icon

fullstackhero.svg is left in public/ for ad-hoc use even though it's
not wired by default.

* feat(docs): drop "/docs" suffix from header brand link

Logo + lowercase fullstackhero wordmark only now — the /docs mono
suffix made sense before there was an icon, but the visual rhythm is
cleaner without it.

* feat(docs): unify all landing sections under Hero's design language

Brings CodeFirst, WhatsIncluded, ModuleShowcase, TechStack, and FinalCta
into the same magazine-style chrome as the Hero — magazine eyebrow rule,
light/bold split headlines with a green gradient underline strip, inline-
bold subtitles, and small editorial flourishes per section.

What landed
- SectionEyebrow refactored from inline pill → full-width magazine rule
  (left primary mono label, hairline rule, optional right meta tag).
- All section containers aligned to max-w-7xl with px-4 sm:px-6 lg:px-8
  (matches the Header so brand logo and section content land on the same
  vertical lines).

Section-by-section
- 01 CodeFirst: light/bold split "One feature. / One folder." Mini-stat
  micro-readout (4 files / feature · 0 project jumps · 1 endpoint / slice)
  ahead of the bullet list, which now uses the star marker SVG. VS-Code
  editor + AnimatedIde wrapper retained.
- 02 WhatsIncluded: "Everything you'd build anyway" with a 3-col grid of
  12 building-block cards. Each card: icon chip + mono category tag
  (AUTH, ISOLATION, DATA, …) + title + body, with a corner-glow hover
  matching the Hero stat cards.
- 03 ModuleShowcase: "Opinionated where it matters." Three large cards
  with icon chip + "MODULE" mono label, tagline, star-marker bullets, and
  a small dark terminal-style code panel (matching the Hero terminal
  aesthetic — bg #0d1117, three traffic-light dots, mono code).
- 04 TechStack: 4-col grid of compact tech cells with icon chip, name,
  version + sub. Hover adds a brand-coloured left accent that slides in.
- 05 FinalCta: "Start where you'd finish." Dotted backdrop, centred
  headline, mono "$ clone the starter kit" prompt over the CopyCommand,
  primary + secondary CTAs, trust strip footer.

All animations are pointer + prefers-reduced-motion aware; magnetic-card
tilt still binds idempotently after astro:after-swap.

Build green; 10 pages built in 2.02s.

* feat(docs): SEO/GEO foundation + fact-checked accuracy pass

Comprehensive SEO/GEO pass aimed at making the docs site the canonical
result for ".NET 10 starter kit" type intents, plus a ground-up
fact-check against the actual repository. Every claim on the landing
now matches what's in the codebase.

ACCURACY FIXES (fact-check against repo)
- Hero stat cards: 3 modules → 10 (Identity, Multitenancy, Auditing +
  Files, Chat, Notifications, Webhooks, Billing, Catalog, Tickets);
  598 tests → 900+ (actual [Fact]+[Theory] count is ~906); "5 min to
  localhost" → "1 command to run" with `dotnet run` boots the stack.
- Hero animated terminal: rewritten to mirror AppHost.cs exactly —
  7 services (postgres, redis, minio, db-migrator, fsh-api, fsh-admin,
  fsh-dashboard). Removed fictional "Ready in 4.2s" timing claim.
- TechStack: Aspire 10.0 → 13.3, EF Core → 10.0.8, Postgres → 17,
  Mediator → 3.0, FluentValidation → 12.1, Scalar → 2.14, Redis
  StackExchange 2.11, OTel 1.15, Finbuckle 10.0, React TS 5.7 — all
  pinned to src/Directory.Packages.props.
- ModuleShowcase Identity: removed "Per-tenant SSO hooks" (not
  shipped, only JWT bearer auth is in the repo) → replaced with
  "Rate-limited login, register & password reset" which is real
  (auth rate-limit policy applied across those endpoints).
- ModuleShowcase: section eyebrow "3 bounded contexts" → "10 modules
  ship in v1"; headline reframed as "Three pillars, seven more in the
  box"; added a four-column aux-module strip below the three pillar
  cards showing Files, Chat, Notifications, Webhooks, Billing,
  Catalog, Tickets with one-line summaries.

NEW SECTIONS (fact-checked content only)
- 05 · WhoItsFor — two-column "Built for / Probably not for" panel.
  Built for: SaaS-on-.NET teams, VSA + sane-defaults teams, indie
  devs, founders who want to own their code. Not for: tiny CRUD,
  Clean-Architecture purists, microservices-day-one, paid-support
  needs. Reduces wrong-fit bounce + builds trust.
- 06 · FAQ — 8-question disclosure list answering the questions
  developers actually ask (free?, prod-ready?, vs ABP?, vs Clean
  Architecture templates?, do I have to use all 10 modules?, SaaS?,
  deployment?, update path?). All answers fact-checked.

SEO/GEO INFRASTRUCTURE
- public/robots.txt — explicit Allow for every major AI/search crawler
  (GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot, anthropic-ai,
  PerplexityBot, Perplexity-User, CCBot, cohere-ai, Bytespider,
  Google-Extended, Applebot-Extended, Bingbot, plus the usual social
  unfurlers). Points at the sitemap.
- public/llms.txt — fact-dense kit summary (architecture, 10 modules,
  12 building blocks, tech stack with versions, how to run, docs map,
  positioning vs ABP / Clean Architecture / commercial competitors).
  Designed to be the single source AI crawlers cite when summarizing
  what the kit is.
- JSON-LD structured data wired through BaseLayout.astro using
  schema.org's @graph: Organization (with logo + sameAs to GitHub +
  codewithmukesh), WebSite, and SoftwareSourceCode (programmingLanguage,
  runtimePlatform, codeRepository, MIT license, author).
- FAQPage JSON-LD attached to the FAQ section so AI engines can
  extract the Q&A pairs directly without scraping the disclosure
  markup.
- theme-color meta tags (light: brand green #15803d, dark: surface
  #0d0e11) for mobile browser chrome.
- og-default.png was 404'ing — pointed ogImage at logo-fullstackhero.png
  so social shares get a real image until a dedicated 1200×630 OG
  card is designed.

H2 + EYEBROW KEYWORD TIGHTENING
- Hero H1: "The .NET starter kit" → "The .NET 10 starter kit"; subtitle
  rewritten as a self-contained citable paragraph that opens "FullStackHero
  is a free, MIT-licensed, production-ready .NET 10 modular monolith…".
- Section eyebrows tightened with literal target-keyword right-meta:
  "Modular Monolith + VSA", "12 shared building blocks", "10 modules
  ship in v1", ".NET 10 · EF Core 10 · Aspire 13".
- WhatsIncluded label: "Everything pre-wired" → "Everything a
  production .NET 10 API needs".

META + DOCUMENT
- siteConfig.title: "FullStackHero — .NET Starter Kit" → "FullStackHero
  — Free .NET 10 Starter Kit" (adds the keyword that gets searched).
- siteConfig.description rewritten to be the same citable paragraph as
  the hero subtitle, with ".NET 10", "free", "MIT-licensed", "ten
  modules" front-loaded.

Build green; 10 pages built in 2.18s. robots.txt + llms.txt served
correctly on the dev server.

* feat(docs): copy rewrite — developer-centric + conversion-focused

Tightens copy across every landing section for three audiences (devs,
tech leads, CTOs), weaves target search phrases naturally, and pushes
the voice closer to "real codebase, not marketing." All claims still
match the repo per the fact-check from the previous commit.

Hero
- H1 bold: "that actually ships." → "you'd actually inherit." Speaks
  to devs ("I won't fight this"), TLs ("team can take it over"), and
  CTOs ("low risk, no lock-in") in three words.
- Subtitle rewritten as a citable definition paragraph:
  "FullStackHero is the free, MIT-licensed .NET 10 starter kit for
  teams shipping production SaaS. Ten modules — identity, multitenancy,
  auditing, files, chat, notifications, webhooks, billing, catalog,
  tickets — wired through a modular monolith with Vertical Slice
  Architecture. A real codebase, not a tutorial. No vendor framework.
  No lock-in." High keyword density, AI-quotable as a single block.
- Stat-card 4 label: "Command to run" → "Command"; note now ends in
  "Aspire 13" instead of "Aspire" for the version anchor.

CodeFirst (01)
- Subtitle: leans harder on the workflow promise — "Add a feature in
  one PR; ship in one merge." Adds tests to the file list. Calls out
  "No jumping between five projects" in bold.

WhatsIncluded (02)
- Subtitle: lists the unglamorous parts by name ("Authentication.
  Authorization. Migrations. Caching. Background jobs. Structured
  logging. Distributed tracing. Idempotency. Webhooks.") and lands
  on "You'd build all of it before your first real feature." More
  specific, less abstract.

ModuleShowcase (03)
- Subtitle: tightened with bold "three questions every B2B SaaS has
  to answer" and "what makes your product yours — not chat, not
  billing, not file uploads." Frames the seven aux modules as time
  saved.

TechStack (04)
- Added a real H2 + subtitle ahead of the grid:
  "First-party Microsoft. / Best-in-class OSS." with the no-lock-in
  pitch ("No proprietary framework. No DSL. No magic. The same
  .NET 10, EF Core 10, Aspire 13, and OSS libraries your team
  already knows — chosen carefully and wired together so they
  actually compose."). Targets the CTO/TL "what are we adopting?"
  audit.

FAQ (06)
- Added two questions that close common gaps:
  - "How does it scale as my product grows?" — frames the modular
    monolith → extract-a-service path honestly.
  - "Can I deploy it to Azure, AWS, or Kubernetes?" — confirms each
    service is a normal Docker image and lists target platforms.

FinalCta (07)
- Subtitle: "scaffolding" → "plumbing"; adds "run one command" to
  reinforce the Aspire single-command boot story.

Build green; 13 pages built in 2.68s.

* feat(docs): redesign sidebar + TOC as numbered chapter cards

Sidebar: warm-paper card per category with single-expand accordion,
monospace 01–13 indices, primary-tinted rail + pill on active item,
auto-expand for the active chapter. Hidden scrollbar with edge-fade
affordance.

TOC: matching card chrome, vertical hairline rail with station-marker
dots on h2, scrollspy via rAF-throttled scroll listener, soft halo on
active dot.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(docs): restructure sections + add full Getting Started flow

Sections now: Getting Started, Architecture, Modules, Building Blocks,
Cross-Cutting Concerns, Security, Frontend, CLI, Testing, Guides,
Deployment, Contributing, Changelog. Drop concepts/reference (folded
into Architecture and Changelog), rename recipes → guides.

Getting Started contains Introduction (moved from /docs/), Prerequisites,
Quick Start, Install. /docs and /docs/getting-started redirect to the
introduction. Header, footer, and landing CTAs rewired to the new paths.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(docs): tighten typography to docs-standard scale

Page H1 32px/600 (was 36px/700), description 15.5px/lh1.55 (was 18px).
Prose H2 22px/600 (was 40px/300), H3 17px/600, H4 15px/600. Body 15px
with lh 1.65 (was 18px/1.75). Matches Stripe/Linear/Anthropic norms;
weight 600 throughout instead of 700.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(docs): switch wrangler to Workers Static Assets

Replace deprecated pages_build_output_dir with [assets] block so
wrangler deploy works (Pages is being unified under Workers).
Rename worker to 'fullstackhero'. Single-quote style to match
Cloudflare's auto-PR generator.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(docs): lowercase fullstackhero brand + GitHub stars on landing

Brand:
- New title: 'fullstackhero - .NET 10 Starter Kit with React UI'.
- Wordmark lowercased everywhere except NuGet package IDs
  (FullStackHero.CLI, FullStackHero.NET.StarterKit must stay PascalCase).
- JSON-LD Organization + SoftwareSourceCode updated.

Social proof:
- helpers/github-stars.ts: build-time fetch of stargazers_count with a
  6500 fallback for offline / rate-limited builds. Memoized per build.
- Header GitHub button: split-pill '[gh] GitHub | ★ 6.5k'.
- Hero chip leads with a tinted star-count pill.
- Hero secondary CTA: 'Star on GitHub | ★ 6.5k' split-pill.
- Primary CTA points to /docs/getting-started/quick-start/.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(docs): mobile docs nav with slide-up sheet

Hidden on lg+, but on phones/tablets the docs main column now has a
trigger pill at the top showing the current section. Tapping opens a
bottom sheet (Stripe/Vercel/Linear pattern) that slides up from the
viewport edge with the full Sidebar inside — same numbered chapter
cards and single-expand accordion.

Affordances:
- Drag handle, close button, tap-scrim to close, ESC to close.
- Tap any link inside → close (route change feels native).
- Active page link is scrollIntoView({block: 'center'}) on open.
- Body scroll lock, inert when closed, focus returns to trigger.

Sidebar now takes a `prefix` prop (default 'fsh', mobile passes
'fsh-m') so aria-controls / labelledby IDs stay unique across the
two simultaneous renderings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(docs): drop placeholder favicon.svg — use brand PNGs/ICO

The favicon.svg in public/ was the old 'FSH on green square'
placeholder. Removing it and dropping its link from BaseLayout so
browsers resolve favicon.ico → 32x32.png → 16x16.png (already the
new brand triangle from the earlier favicon commit).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(docs): tier-1 SEO + GEO code wins

Six concrete improvements aimed at unlocking AI citability and search
ranking on top of the editorial landing already in place.

1) Per-page SEO meta (content.config.ts + DocsLayout)
   Extends the docs content schema with an optional seo block:
   { title, description, keywords, ogImage, noindex }. Each field
   falls through to the frontmatter / site defaults so existing pages
   keep working. The [...slug] route now plumbs seoTitle and
   additionalSchemas through DocsLayout → BaseLayout so individual
   docs pages can target their own keyword-rich <title> independent
   of the on-page H1.

2) SoftwareApplication schema upgrade (BaseLayout)
   Swapped the homepage's SoftwareSourceCode entity for the more
   AI-citable SoftwareApplication, with applicationCategory
   "DeveloperApplication", operatingSystem, softwareVersion 10.0.0-rc.1,
   softwareRequirements (.NET 10 SDK, PostgreSQL 17, Redis 7, Node 20+),
   downloadUrl, and crucially:
     offers: { price: "0", priceCurrency: "USD", availability: InStock }
   That's the marker Google uses to label software listings as "Free"
   in rich results.

3) BreadcrumbList JSON-LD on every docs page (helpers/breadcrumb.ts)
   New buildBreadcrumbItems + buildBreadcrumbSchema helpers derive
   breadcrumbs from the URL pathname, title-casing each segment, and
   override the final crumb name with the page's actual title.
   Emitted via additionalSchemas alongside a TechArticle schema
   carrying headline, description, dateModified / datePublished, image,
   author, publisher, and inLanguage — generic enough to cover
   guides, references, concepts, and recipes.

4) llms-full.txt generated at build time (pages/llms-full.txt.ts)
   New static endpoint that concatenates every docs collection entry
   as plain text — title, blockquote description, canonical URL, raw
   markdown body. Per-page sections separated by `---` rules so AI
   crawlers can ingest the entire docs corpus in a single fetch.
   Builds 429-line file at /llms-full.txt covering 16 pages.

5) Sitemap priorities + changefreq (astro.config.mjs)
   serialize() callback on @astrojs/sitemap sets per-URL priority +
   changefreq:
     /              → 1.0 / weekly
     /docs/getting-started/* → 0.9 / monthly
     /docs/modules|building-blocks|architecture/* → 0.8 / monthly
     /docs/guides/* → 0.75 / monthly
     /docs/security|deployment|frontend|testing|cli/* → 0.7 / monthly
     /docs/changelog/* → 0.5 / weekly  (freshness signal)
     /docs/contributing/* → 0.4 / yearly
     /docs/*        → 0.6 / monthly (fallback)

6) Cross-link landing → docs (WhatsIncluded + FAQ)
   - Each WhatsIncluded card now click-throughs to the most relevant
     /docs/ page (12 new internal links from a high-PR landing page).
     Cards reveal a "Read the docs →" affordance on hover.
   - Section footer adds "Browse the full building-blocks reference →"
     CTA pointing at /docs/building-blocks/.
   - Each FAQ answer gets an optional link object rendered as a
     "Read the X →" link below the prose. Adds 9 more internal links
     to deeper docs (testing, architecture, modules, deployment,
     changelog, cross-cutting-concerns).

Build green; 18 pages built in 2.93s. Verified: docs pages emit
3 application/ld+json blocks (site graph + BreadcrumbList + TechArticle),
homepage emits site graph + FAQPage, sitemap carries priority + changefreq
per URL, /llms-full.txt is 429 lines covering all 16 docs pages.

* feat(docs): nav Docs link points straight to introduction

Header / Footer / Breadcrumbs all link the "Docs" entry directly at
/docs/getting-started/introduction/ (no redirect hop). Header keeps
the previous active-state semantics via a new `match` field on the
nav item — Docs still highlights for any /docs/* path even though
its link target is deeper.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(docs): section index pages with auto-generated card grids

Each category now has a real index page that lists its child pages as
a card grid (2-col / 1-col mobile). Cards show a monospace 01/02/…
index, the page title, its description, and a chevron that nudges on
hover. Empty-state placeholder card renders for sections that don't
have child pages yet.

- New SectionIndex.astro: discovers pages in src/content/docs/{section},
  sorts by sidebar.order, excludes the index itself + hidden pages.
- Registered as a global MDX component (no per-file imports needed).
- Re-created getting-started/index.mdx + dropped the
  /docs/getting-started/ redirect so the new index is reachable.
- Updated 10 other section index pages to use SectionIndex.
  contributing + changelog kept as single-page sections.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(docs): track-1 content — comparison pages + getting-started SEO

The first content writing pass. Adds three full comparison pages that
rank for "X alternative" queries, wires them into the sidebar under
a new Compare section, and adds per-page SEO frontmatter to the four
Getting Started pages.

Comparison pages (~1100-1400 words each, fact-checked against repos)
- /docs/compare/fsh-vs-abp/ — biggest direct competitor. Honest
  side-by-side (license, code ownership, DI, mediator, multitenancy
  stack, stars). "When to choose ABP / when to choose fullstackhero"
  with fair trade-offs. Migration notes (modules map, repositories
  become DbContexts, app services become Mediator handlers).
- /docs/compare/fsh-vs-clean-architecture/ — Jason Taylor (~20.1k★)
  and Ardalis (~18.2k★) templates. Frames the difference as "they
  ship an architecture; fullstackhero ships ten production modules
  on top of an architecture." Acknowledges where CA templates are
  the better classroom + the better fit for small focused services.
- /docs/compare/fsh-vs-blazorplate/ — paid closed-source ($499-$999)
  vs free open-source MIT. React vs Blazor, webhooks + OpenTelemetry
  deltas, hybrid-use guidance.

Each comparison page ships honest disclaimers ("this page is
maintained by us, open an issue if anything is unfair") and links to
the competitor's canonical site/repo. All Q→A claims are checked
against either upstream repos or our Directory.Packages.props.

Compare section
- New /docs/compare/index.mdx — section landing with a quick decision
  tree pointing the reader at the right comparison.
- _sections.ts gains a 'compare' entry at order 12, between Deployment
  and Contributing — sidebar wiring automatic via the section list.
- astro.config.mjs sitemap rule adds /docs/compare/* → priority 0.8 /
  monthly. These are high-conversion + rank for head-intent "X
  alternative" queries; deserve the bump.

Getting Started SEO frontmatter
- Each of the four existing getting-started pages (introduction,
  prerequisites, install, quick-start) gains a seo block with
  keyword-targeted title, description, and keywords. The on-page H1
  stays editorial; the <title> tag and meta description target
  long-tail searches like "fsh CLI", ".NET Aspire workload",
  "FullStackHero.NET.StarterKit dotnet new template".

Verified
- Build green; 24 pages built in 3.17s (up from 18 last pass).
- Sitemap carries all four compare URLs at priority 0.8.
- llms-full.txt picked up the new content automatically (no code
  change needed — the static endpoint enumerates the docs collection).
- FSH CLI claims verified: `FullStackHero.CLI` package + `fsh` tool
  command in src/Tools/CLI/FSH.CLI.csproj, `fsh new` + `fsh doctor`
  commands in Commands/.

* feat(docs): /docs/ overview hub with chapter-plate category cards

/docs/ is now a real overview page that lists every category as a card
grid. Each card carries a lucide icon, a huge ghosted watermark numeral
(editorial accent), title, description, live page count, and an
orchestrated hover sequence (lift + border + icon tint + watermark
glow + arrow nudge on a single 320ms curve).

- CategoryIndex.astro: discovers sections from _sections.ts, pulls
  descriptions from each section's index.mdx, counts child pages.
- Registered as a global MDX component.
- docs/index.mdx (new) renders the hub.
- Header / Footer / Breadcrumbs re-point the "Docs" entry at /docs/.
- Typography: body-font title, sentence-case page-count meta (no more
  mono-caps eyebrow / slug).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(docs): wire .not-prose utility so card grids escape prose styles

The card grids inside MDX pages were inheriting prose-context styles —
the star li::before marker (rendering as little green +/sparkle marks
around each card) and the :where(a) underline. The .not-prose class
on the grids was a no-op because there was no actual rule wiring it.

Added a real .not-prose rule in globals.css that resets list-marker,
list padding, and anchor text-decoration for any descendant subtree
marked not-prose. SectionIndex picks up a belt-and-braces inline
text-decoration:none to defeat any stray :where(a) cascade.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(modules): 10 module deep-dive pages — full first drafts

One MDX per module under /docs/modules/. All claims fact-checked
against the runtime source — every endpoint table, version pin,
domain-model excerpt, and configuration block was verified before
landing in prose. Each page targets a specific long-tail search
phrase via its seo.title and runs 1,000-1,800 words.

The ten pages
- identity.mdx     (order 1) — JWT + refresh + permissions + groups +
  impersonation + 2FA + sessions + password policy. ~1,800 words.
  48 endpoints tabulated. References IdentityModule.cs + Domain/
  FshUser.cs + Contracts/v1/Tokens/.
- multitenancy.mdx (order 2) — Finbuckle strategy chain + cache store +
  EFCoreStore + per-tenant connection strings + provisioning state
  machine + tenant themes. ~1,400 words. 9 endpoints. Documents the
  root-operator header override + claim-strategy-pre-auth gotcha.
- auditing.mdx     (order 3) — SaveChanges interceptor + HTTP
  middleware + fluent Audit builder + JSON masking + channel
  publisher + SQL/DLQ sinks + retention job. ~1,500 words. All
  seven read endpoints + the AuditHttpOptions / AuditRetentionOptions
  configuration blocks documented verbatim.
- files.mdx        (order 4) — presigned URL flow + per-OwnerType
  IFileAccessPolicy + finalize state machine + Files Categories
  config + IFileScanner hook + orphan/retention purge jobs +
  FileFinalizedIntegrationEvent. ~1,300 words.
- chat.mdx         (order 5) — three channel kinds + idempotent
  DirectKey for DMs + tombstone soft-delete + threads + reactions
  + pinning + mention parser + AppHub realtime + Redis backplane
  + 3s typing throttle. ~1,500 words. 22 endpoints. Documents the
  module-order quirk (Notifications 750 before Chat 800).
- notifications.mdx (order 6) — denormalized inbox row + integration
  event bridge + SignalR push to user:{id} + idempotent mark-read +
  bulk ExecuteUpdateAsync mark-all-read. ~1,000 words. 4 endpoints.
  Includes step-by-step "add a new notification type from another
  module" extension recipe.
- webhooks.mdx     (order 7) — tenant-scoped subscriptions + open-
  generic WebhookFanoutHandler<TEvent> + HMAC-SHA256 signing +
  Hangfire AutomaticRetry with 30s/2m/10m/1h backoff + delivery log.
  ~1,400 words. 5 endpoints. Documents the "restore tenant context
  manually" gotcha + ships a complete C# signature-verification
  recipe for subscribers.
- billing.mdx      (order 8) — already shipped in prior commit
- catalog.mdx      (order 9) — already shipped in prior commit
- tickets.mdx      (order 10) — already shipped in prior commit

Across all ten pages we now document
- 234 endpoints (verified from MapEndpoint extensions)
- 30+ aggregate / entity types (with sealed-class + invariant excerpts)
- 100+ commands / queries from the Contracts assemblies
- Every IOptions<T> config block with appsettings keys
- Every Hangfire recurring job by cron expression
- Every IGlobalEntity / tenant-context quirk worth knowing

Build green; 34 pages built in 4.75s (up from 27 last pass).

* docs(building-blocks): 12 building-block reference pages

One MDX per FSH.Framework block under /docs/building-blocks/. Each
page documents the block's public extension methods, interfaces, base
types, options, and consuming-module examples — fact-checked against
the BuildingBlocks/ source.

The twelve pages
- core.mdx                    (1)  BaseEntity, AggregateRoot, DomainEvent,
                                   ICurrentUser, CustomException hierarchy
- persistence.mdx             (2)  BaseDbContext + auto-applied filters,
                                   Specification<T>, audit + domain-event
                                   interceptors
- shared.mdx                  (3)  AppTenantInfo, PermissionConstants,
                                   FshPermission, claim/action/resource
                                   constants, audit attributes
- web.mdx                     (4)  AddHeroPlatform / UseHeroPlatform,
                                   ModuleLoader, GlobalExceptionHandler,
                                   ValidationBehavior, CurrentUserMiddleware
- caching.mdx                 (5)  HybridCache + ObservableHybridCache
                                   decorator with OTel; stampede protection
- eventing-abstractions.mdx   (6)  IIntegrationEvent, IIntegrationEventHandler,
                                   IEventBus, IEventSerializer (dep-free)
- eventing.mdx                (7)  InMemoryEventBus, RabbitMqEventBus,
                                   EfCoreOutboxStore, EfCoreInboxStore,
                                   OutboxDispatcher
- jobs.mdx                    (8)  Hangfire wiring + FshJobActivator +
                                   FshJobFilter (tenant context) + dashboard
                                   basic auth + stale-lock cleanup
- mailing.mdx                 (9)  IMailService + SMTP (MailKit) and
                                   SendGrid implementations
- storage.mdx                 (10) IStorageService + LocalStorageService
                                   and S3StorageService + presigned URLs
                                   + QuotaMeteredStorageService decorator
- quota.mdx                   (11) IQuotaService (Redis/InMemory/Noop) +
                                   plan resolution + gauge providers +
                                   enforcement middleware
- blazor-ui.mdx               (12) Placeholder — v1 ships React frontends;
                                   block reserved for future Blazor lib

Per page: ~700-1,200 words covering purpose, public surface, options,
how modules consume it, extensibility recipes, and gotchas. Every
extension method, interface, and option type listed has a file path
in the appendix.

Cross-link density between blocks and modules is dense — Storage links
to Files, Quota links to Identity's user gauge + Billing's plan keys,
Eventing links to Notifications + Webhooks, etc.

Build green; 46 pages built in 5.73s (up from 34).

* docs(architecture): 4 architecture pillar pages

Synthesizes patterns documented across the module and building-block
pages into four conceptual deep-dives at /docs/architecture/.

- modular-monolith.mdx       (order 2) — the outer shape: ten modules
  with one-way Contracts boundaries enforced by NetArchTest, load
  order, three patterns for cross-module talk (service interfaces,
  domain events, integration events), and the extract-a-service
  migration path when scaling demands it. ~1,400 words.
- vertical-slice.mdx         (order 3) — the inner shape: one feature
  per folder with endpoint + command + handler + validator + tests.
  Complete RegisterUser end-to-end excerpt. Conventions (sealed,
  ValueTask, ConfigureAwait(false), one endpoint per slice).
  Trade-offs vs Clean Architecture. ~1,200 words.
- multitenancy-deep-dive.mdx (order 4) — tenancy as default. Three
  layers (HTTP/data/jobs), Finbuckle strategy chain + claim-pre-auth
  gotcha, BaseDbContext.ApplyTenantIsolationByDefault(), IGlobalEntity
  opt-out, named SoftDelete filter, cross-tenant query pattern
  (IgnoreQueryFilters + explicit re-filter), per-tenant connection
  strings, tag-based cache eviction. ~1,400 words.
- dependency-injection.mdx   (order 5) — composition root: 4-line
  Program.cs, the IModule contract, Mediator 3 source-gen MSG0007
  gotcha, FluentValidation auto-registration, three lifetimes, the
  four wire points for a new module (with the Mediator-list silent-
  failure flag), middleware pipeline order including CORS-before-
  HTTPS-redirect and CurrentUserMiddleware-last rules. ~1,300 words.

Build green; 50 pages built in 6.11s (up from 46).

* feat(docs): section cards match chapter-plate aesthetic + fix not-prose

SectionIndex cards now use the same modern design language as the
CategoryIndex hub: huge ghosted watermark numeral as the editorial
accent, body-font title, line-clamped description, pagination eyebrow
(01 / 04), and a unified hover sequence on a single 320ms curve.

Also fixes the .not-prose escape hatch. Previous selectors required
.not-prose to be an *ancestor* of the <ul>, but the class lives ON
the <ul> itself — so prose's star li::before marker kept leaking
into both card grids. Rewrote with :where(.not-prose) selectors that
match either placement; `!important` plus :where()'s 0-specificity
cleanly defeats the prose cascade.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(sections): testing + security + cross-cutting-concerns full pages

Replaces three stub section indexes with substantial fact-checked
content. Each page now stands as a complete reference; the
<SectionIndex> wiring is removed since these are no longer
just landing pages for child docs.

testing/index.mdx (~1,800 words)
- Three-layer model: unit (xUnit + Shouldly + NSubstitute + AutoFixture),
  integration (WebApplicationFactory + Testcontainers Postgres/Redis/
  MinIO), architecture (NetArchTest).
- The test stack pinned in Directory.Packages.props, the test project
  layout under src/Tests/, the naming convention "Method_Should_X_When_Y".
- Real RegisterUserCommandHandlerTests + ProductsEndpointTests
  excerpts as templates.
- All 9 NetArchTest rule families enumerated (ModuleBoundary,
  ContractsPurity, BuildingBlocksIndependence, CircularReference,
  HandlerValidatorPairing, DomainEntity, ApiVersioning, FeatureFolder,
  HostArchitecture). 48 architecture tests total.
- "How to add a new test" recipes for each layer.
- Honest disclaimers: no load tests, no visual regression, no chaos.

security/index.mdx (~2,000 words)
- The eight pillars: authentication, authorization, tenant isolation,
  audit, rate limiting, webhook integrity, secret protection, data
  masking.
- JWT bearer + rotating refresh, 5-attempt lockout, password policy
  (12-char min, 5-history, 90-day expiry), email confirmation,
  optional 2FA TOTP.
- Permission-based authorization via .RequirePermission(); roles as
  groupings, permissions as the gate; the RequiredPermissionAttribute
  silent-no-op gotcha flagged inline.
- Tenant isolation default; IgnoreQueryFilters discipline; named
  SoftDelete filter; IGlobalEntity opt-out.
- Impersonation grant lifecycle + revocation list.
- Rate-limit auth policy on 6 endpoints enumerated.
- HMAC-SHA256 webhook signing + full subscriber-side verifier excerpt.
- Redis-backed Data Protection key persistence.
- Audit + JSON masking + retention.
- CORS-before-HTTPS-redirect + SignalR credentialed CORS gotcha.
- A 10-item day-one production security checklist.

cross-cutting-concerns/index.mdx (~1,800 words)
- The 11 platform features in a single table + per-feature deep dives.
- HybridCache stampede protection + tag-based eviction + shared
  multiplexer rationale.
- Hangfire FshJobFilter (tenant context), FshJobActivator (scoped DI),
  HangfireTelemetryFilter, plus the 5 recurring jobs the kit ships.
- OpenTelemetry: traces (incl. MediatorTracingBehavior), metrics,
  logs over OTLP; Serilog enrichers.
- Idempotency-Key replay via HybridCache.
- Feature flags with TenantFeatureFilter for per-tenant overrides.
- Rate limiting (vs Quota — different concerns).
- Health checks: db, redis, MinIO, tenant migrations.
- SSE + SignalR + Redis backplane + AppHub group conventions.
- HTTP resilience pipeline (Polly v8 via Microsoft.Extensions.Http.Resilience).
- ProblemDetails (RFC 9457) global handler + Mediator pipeline
  behaviors (ValidationBehavior, MediatorTracingBehavior).

All claims fact-checked against the source — versions pinned from
Directory.Packages.props, recurring-job crons verified from each
module's WebhooksModule.cs / BillingModule.cs / FilesModule.cs / etc.,
NetArchTest rule families verified from src/Tests/Architecture.Tests/.

Build green; 50 pages (unchanged — these replaced existing stubs).

* docs(cross-cutting): split into 11 per-topic pages

Replaces the single-page cross-cutting-concerns index with one MDX
per concern under /docs/cross-cutting-concerns/. Each page is now
rank-able for its own long-tail search query and the section index
auto-lists children via <SectionIndex>.

The eleven new pages
- caching.mdx          (1)  HybridCache L1+L2 + stampede + tag eviction
                            + shared multiplexer
- background-jobs.mdx  (2)  Hangfire + FshJobFilter (tenant context) +
                            FshJobActivator (scoped DI) + the 5 cron
                            jobs the kit ships + retry attributes
- observability.mdx    (3)  Serilog enrichers + OpenTelemetry 1.15 +
                            instrumentation matrix + MediatorTracing
- idempotency.mdx      (4)  Idempotency-Key header + HybridCache replay
                            + retention window + content-dedupe disclaimer
- feature-flags.mdx    (5)  Microsoft.FeatureManagement + TenantFeatureFilter
                            + staged rollout / kill switch / per-env patterns
- rate-limiting.mdx    (6)  auth policy on 6 endpoints + AuthRateLimitWiringTests
                            + adding custom policies + monitoring 429s
- health-checks.mdx    (7)  /health and /health/ready + per-DbContext +
                            Redis + MinIO + TenantMigrationsHealthCheck
                            + k8s liveness/readiness probe wiring
- server-sent-events.mdx (8) SSE primitive + browser EventSource client +
                            ?access_token= auth + heartbeat config
- realtime.mdx         (9)  SignalR over Redis backplane + AppHub group
                            conventions (user:{id} + channel:{id}) +
                            Context.User vs ICurrentUser gotcha +
                            TestServer-no-WebSocket integration testing
- http-resilience.mdx (10)  Polly v8 standard pipeline + HTTP-layer vs
                            job-layer retry split + circuit breaker +
                            hedging + non-idempotent retry pitfall
- error-handling.mdx  (11)  GlobalExceptionHandler + RFC 9457
                            ProblemDetails + the 4-exception hierarchy +
                            FluentValidation field errors + throw-from-
                            domain idiom

Index page is now slim — short intro + <SectionIndex> auto-listing.

Build green; 61 pages built in 8.19s (up from 50).

* docs(security): split into 8 per-topic pages

Replaces the single-page security index with one MDX per concern.

The eight new pages
- authentication.mdx       (1) JWT bearer + rotating refresh + lockout +
                               password policy + email confirmation flow
- authorization.mdx        (2) Permission-based gates, role+group aggregation,
                               and the silent-no-op gotcha for duplicate
                               RequiredPermissionAttribute declarations
- impersonation.mdx        (3) Time-bound grants, IGlobalEntity persistence,
                               jti revocation list, cross-tenant root override,
                               full audit trail
- two-factor.mdx           (4) TOTP enrol/verify/disable; recovery-codes
                               gap flagged as recommended add-on
- webhook-signing.mdx      (5) HMAC-SHA256 signing scheme + both C# and
                               Node/Express subscriber-side verification
                               recipes + replay protection via delivery id
- data-protection.mdx      (6) Redis-backed key persistence so cookies +
                               antiforgery + email tokens survive rolling deploys
- cors-and-headers.mdx     (7) CORS-before-HTTPS-redirect ordering, the
                               SignalR-credentialed-CORS gotcha, full CSP +
                               HSTS + X-Frame-Options reference
- production-checklist.mdx (8) The 10-item release gate — JWT rotation,
                               password policy, CORS, headers, rate limits,
                               HTTPS, debug-endpoint lockdown, Hangfire auth,
                               Data Protection persistence, audit retention

Index page is now a slim landing with <SectionIndex> auto-listing.

Build green; 69 pages built in 7.27s (up from 61).

* docs(testing): split into 6 per-topic pages

Replaces the single-page testing index with one MDX per layer plus
meta pages (fixtures, running, writing).

The six new pages
- unit-tests.mdx            (1) xUnit + Shouldly + NSubstitute +
                                AutoFixture; Method_Should_X_When_Y naming;
                                three ground rules (Shouldly, direct SUT,
                                no infrastructure); aggregate-invariant +
                                validator + mock-verification patterns
- integration-tests.mdx     (2) WebApplicationFactory + Testcontainers
                                Postgres/Redis/MinIO; FshWebApplicationFactory
                                + Seed.* + CreateAuthenticatedClient helpers;
                                full ImpersonationTests example
- architecture-tests.mdx    (3) 9 rule families + 48 tests; module-boundary
                                + endpoint-convention + feature-folder
                                examples; "specification in XML doc + assertion
                                in code" pattern; what arch tests don't cover
- fixtures-and-seed-data.mdx (4) DatabaseFixture + RedisFixture + MinioFixture
                                composition; FshWebApplicationFactory wiring;
                                Seed.* static accessor; insert-don't-mutate
                                discipline; common mistakes
- running-tests.mdx         (5) Commands (everything, single project, filter,
                                verbose, coverage); Docker requirement; full
                                GitHub Actions workflow; CI sharding pattern;
                                container cleanup
- writing-new-tests.mdx     (6) Three step-by-step recipes: unit (with full
                                handler example), integration (with full
                                endpoint example), architecture rule (with
                                IL-walker disclaimer)

Index page is now a slim landing with <SectionIndex>.

Build green; 75 pages built in 8.60s (up from 69).

* docs(frontend + guides): impersonation walkthrough + frontend split + screenshot placeholders

Adds a comprehensive operator impersonation guide and splits the
frontend section into per-app pages. New <Screenshot> MDX component
renders styled placeholder boxes when the image asset doesn't exist
yet — owner can drop screenshots into docs/public/screenshots/ and
the placeholders flip to real images automatically.

New MDX component
- components/docs/Screenshot.astro — file-exists check at build time;
  styled dashed-border placeholder when missing, real <img> + caption
  when present. Wired into mdxComponents export.

New guides page
- guides/operator-impersonation.mdx (~1,700 words) — end-to-end UI
  walkthrough: open admin → find user → start grant (with mandatory
  reason) → act as user in new tab → end or revoke → audit trail
  query. 9 screenshot placeholders covering every step. Plus a
  cross-tenant section explaining the root-operator override, audit
  query examples (Identity.Impersonation events), operational policies
  (mandate reason, short duration, alert on patterns, train operators),
  and 4 common-question answers (notified? chained? self? lost device?).

Frontend section — 5 sub-pages + slim index
- admin.mdx           (~1,500 words) — operator-facing app: 11 page
  areas, JWT auth flow with refresh, permission-gated UI, TanStack
  Query patterns, SignalR notifications, the impersonation modal +
  flow + audit, theme editor for tenants. 8 screenshot placeholders.
- dashboard.mdx       (~1,400 words) — tenant-facing app: catalog,
  chat (the densest realtime page), files presigned upload, tickets,
  identity (profile + sessions + 2FA). 12 screenshot placeholders
  covering every major page.
- architecture.mdx    (~1,500 words) — shared patterns across both
  apps: folder layout, provider stack order, apiClient interceptors
  (auth + refresh + ProblemDetails), TanStack Query conventions,
  RouteGuard, RealtimeProvider, env handling.
- theming.mdx         (~1,200 words) — per-tenant theming via CSS
  custom properties, the 9 palette roles (light + dark), brand assets,
  typography, layout. Theme editor UI walkthrough with 4 screenshot
  placeholders. Loading-flash mitigation pattern + dark mode story.
- development.mdx     (~1,400 words) — Vite npm scripts, Aspire-
  orchestrated vs npm-direct dev, production build, runtime config via
  envsubst (full Dockerfile + entrypoint.sh), Nginx SPA config,
  Vercel / Netlify / S3+CloudFront / Cloudflare Pages deployment shapes,
  Vitest setup, common issues.

Total: ~33 screenshot placeholders embedded across the new pages.
Each Screenshot has a target path under public/screenshots/ — drop
images there and they replace the placeholders automatically.

Build green; 81 pages built in 9.40s (up from 75).

* fix(docs): hide screenshot placeholders in production builds

The <Screenshot> placeholder box now renders only in dev (\`astro dev\`)
where the author needs to see what's still missing. Production builds
(\`astro build\`) emit nothing when the asset doesn't exist — so missing
screenshots leave no trace in shipped docs.

The "image exists" path is unchanged; once a screenshot lands at the
expected path under docs/public/screenshots/, the real <img> renders
in any environment.

Toggle uses import.meta.env.DEV, which Astro/Vite sets to true under
the dev server and false during production builds.

Verified: 0 .screenshot-placeholder elements in the production build
across operator-impersonation, admin, dashboard, theming pages.

* feat(dashboard+backend): v1 UI polish, file visibility/sharing, permission catalog

Dashboard: revert warm-paper-hue chassis to untinted neutrals; polish list
primitives (combobox, density-toggle, empty-state, hero, pagination, sort,
stat) and add entity-detail/entity-shell/tone-icon-tile; tighten auth shell,
command palette, file pickers, notifications, sidebar/topbar, theme toggle;
page-level passes across activity, audits, auth, catalog, chat, files, login,
settings, system, tickets.

Files: add ChangeFileVisibility command + ListSharedFiles query, surface
visibility on FileAssetDto.

Identity: add GetPermissionCatalog query/endpoint; RoleService surfaces
catalog data.

Tests: add PermissionCatalogTests; add adminPassword to
TenantCreation/TenantActivation payloads; update RoleManagementTests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(tests): update role helpers to deserialize PagedResponse<RoleDto>

GET /roles now returns PagedResponse<RoleDto> (paged) instead of a flat
RoleDto[]; three integration test helpers still expected the array
shape, blowing up on the first byte of every response and failing 8
tests across the Roles and Users suites.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(dashboard): A+ rollup — deps + a11y + perf + tokens + UX

Closes the P0 + most P1 items from the 2026-05-21 dashboard audit
(docs/superpowers/audits/2026-05-21-dashboard-world-class-audit.md).

Bundle: main JS 197.5 KB -> 178.0 KB gzip (-10%). New lazy chunks:
command-palette-dialog 8.2 KB, SignalR 14.5 KB. Chat-specific CSS
extracted to a 3.9 KB per-route chunk. Main CSS 127.5 -> 121.4 KB.

Build + lint + Playwright (37/37) all green. Zero npm advisories.

Dependency hygiene
- Wave-1 bumps: react 19.2.6, react-dom 19.2.6, @tanstack/react-query
  5.100.11, @tanstack/react-virtual 3.13.25, react-router-dom 7.15.1,
  tailwindcss 4.3.0, @tailwindcss/vite 4.3.0, tailwind-merge 3.6,
  vite 7.3.3, @vitejs/plugin-react 4.7, typescript 5.9.3,
  typescript-eslint 8.59.4, eslint 9.39.4, plus @types/react,
  @types/react-dom, @types/node patches.
- Removed unused deps: recharts (declared, zero imports),
  react-hook-form, @hookform/resolvers, zod, @types/lodash,
  autoprefixer (Tailwind v4 handles prefixing natively).

Dead code
- Deleted 10 orphan files (zero cross-grep consumers):
  file-gallery, sse/live-feed, sse/sse-status-badge, theme-toggle,
  list/density-toggle, list/empty-state, list/list-hero,
  list/pagination, list/sort-chips, list/stat. components/sse/
  directory removed.
- Pruned components/list/index.ts barrel. Removed unused API
  exports: getAuditsByTrace, reorderProductImages, discoverChannels,
  restoreChannel, getRoleById. Dropped unused export keywords
  (BRAND_LADDER).

Tokens + visual cohesion
- Dropped backdrop-grayscale from the Dialog scrim (it was
  desaturating the entire page behind every modal).
- Light-mode elevation restored: --surface-3 now differs from
  --card / --background so cards float again.
- Muted-foreground bumped (dark L 0.680 -> 0.730; light 0.575 ->
  0.500) so placeholders + chevrons clear WCAG 2.2 AA 4.5:1.
- Added canonical type scale: --text-display-page/section/card/stat
  (Tailwind v4 auto-generates the text-display-* utilities).
- Defined missing fsh-sheet-in/out keyframes for the mobile drawer
  (previously referenced but undefined).
- Replaced 19 shadow-[0_1px_2px_oklch(...0.04)] literals across
  12 files with the shadow-xs token. Fixed clipped oklch lightness
  in overview hover-borders (now uses --color-border-strong).
- Removed dead .chat-empty-hero rule.

Type scale migration
- Migrated h1s in EntityPageHeader, PageHero, OverviewPage,
  NotFoundPage to the new tokens. Chat channel header demoted
  h1 -> h2 so each route has a single h1.

Fonts
- index.html eager Google Fonts trimmed from 12 families to 3
  (Figtree + Outfit + JetBrains Mono). The other 9 are lazy-injected
  by ensureLazyFontsLoaded() when Appearance settings mounts, or
  synchronously at boot when the user has a non-default body font
  stored in localStorage. ~200-400 KB cold-load savings.

Accessibility (WCAG 2.2 AA)
- Programmatic-label fixes: aria-label on cmdk Command.Input + chat
  composer textarea; aria-expanded={!collapsed} on sidebar collapse
  button.
- FilePreviewDialog no longer renders a whitespace-only
  DialogDescription (SR-read as empty description).
- Field primitive: required indicator gets an sr-only "required"
  sibling alongside the visual dot.
- RouteError stack trace gated behind import.meta.env.DEV — prod
  users see a recoverable card with no internals exposed.
- RealtimeStatusPill: new `announce` prop, single mount (bell footer)
  now owns the live region — the chat-rail instance stops
  duplicate-announcing reconnect events.
- role="log" aria-live="polite" on chat MessageList scroll
  container + Activity feed (mobile cards + desktop table).
- role="status" aria-live="polite" on typing-indicator.
- Touch targets bumped from h-5 w-5 (20px) to h-6 w-6 (24px) on
  composer reply-quote/attachment/upload clears, channel-rail
  filter clear + section-action plus, login eye-toggle, with
  focus-visible rings added where missing.

Forms
- aria-invalid + aria-describedby wired on login + forgot-password
  + reset-password forms so SR users hear errors linked to the
  fields they apply to. Sibling error blocks got stable ids.

Performance
- @microsoft/signalr is now dynamic-imported inside
  realtime-context's connect flow (37 KB gzip off the main shell).
  HubConnectionState comparisons rewritten as string-literal so the
  shutdown / invoke paths don't pull SignalR back in.
- CommandPaletteDialog extracted to its own file and React.lazy
  imported by CommandPaletteRoot — cmdk + the full action graph
  load on first Cmd+K, not on cold start.
- SseContext split into useSseStatus (status + eventCount) and
  useSseEvents (events array). Topbar SSE dot + bell pill stop
  re-rendering on every SSE event; only LiveFeed / Activity /
  Overview's livefeed-body subscribe to the events stream.
- Avatar <img> gained loading="lazy" + decoding="async" + explicit
  width/height (matches sizeClass), eliminating CLS during avatar
  swap-in.

UX
- Command palette expanded: Identity (users/roles/groups), Catalog
  (products/brands/categories), Tickets, Files, Chat, Trash,
  Sessions, Appearance — every leaf route is now navigable from
  Cmd+K. New Create group with 9 entries (user/role/group/product/
  brand/category/ticket/channel/file-upload).
- Tickets list + detail render assignee/reporter via useUserDisplay
  (real names + avatars) instead of authorUserId.slice(0,8) + "...".
- Chat-specific CSS rules (typing dots, unread divider, day rule,
  reaction chip, jump pill, mention pill) moved to
  pages/chat/chat.css — Vite extracts them into the chat-page CSS
  chunk so other pages stop shipping ~3 KB of selectors they don't
  use. .chat-status-pill stays in globals.css because the global
  notification bell consumes it.

ESLint
- jsx-a11y/no-noninteractive-element-interactions promoted from
  warn -> error (configured to exclude onError so legitimate image
  fallback handlers don't trip). Added no-noninteractive-element-
  to-interactive-role / no-aria-hidden-on-focusable / anchor-has-
  content as error. Fixed the one violation in user-picker.tsx
  (ul role=listbox -> div role=listbox).

Tests
- Refreshed two Playwright auth specs (forgot-password,
  reset-password) that were asserting the old "// 02.FORGOT-
  PASSWORD" / "// 03.RESET-PASSWORD" eyebrow chrome and <code> chip
  rendering that the v1 AuthShell redesign (commit 519d664d)
  retired. Tests now target the current headline + body text.

Docs
- Added docs/superpowers/audits/2026-05-21-dashboard-world-class-
  audit.md (the original six-agent composite audit, updated with a
  Status preamble noting what landed in this rollup).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(docs): page view counter (D1) + frontmatter dates + hero copy

Add a Cloudflare D1-backed page view counter to the docs site:
- worker.ts handles POST/GET /api/views with KV per-(IP+UA) dedup (1h TTL)
  and delegates all non-API paths to the static assets binding
- migrations/0001_init.sql defines the views table (+ count index)
- client beacon (views-counter.ts) posts the slug and renders the count
  into a PageHeader chip with an eased count-up reveal; dev shows a sample,
  prod stays hidden until the beacon resolves
- wrangler.toml wires the worker entry, assets, D1, and KV bindings

Move page dates into MDX frontmatter (lastUpdated), backfilled across all
79 docs from git history, and remove the broken remark-modified-time plugin
(Astro 6's glob loader discards remark frontmatter mutations, so the value
never reached entry.data).

Reword the hero headline to "built to ship, not to demo."

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(identity,multitenancy): propagate CancellationToken through service layer + endpoints

Full-sweep CancellationToken audit (Roslyn navigator AP009). Adds
`CancellationToken cancellatio…
@iammukeshm
Copy link
Copy Markdown
Member

I will need you to split the PRs for better visibility. Won't be able to review 1k files in a go ;)

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