merge (#1)#1278
Open
guneyunus wants to merge 1 commit into
Open
Conversation
* 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…
Member
|
I will need you to split the PRs for better visibility. Won't be able to review 1k files in a go ;) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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:
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 programmaticpath.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:
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.
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.
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:
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.
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. Atdocker runtime the daemon could not resolve that "user" and exited withunable 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_UIDbecause Docker's parser does expand ENV vars inside the USER directive at build time.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.
Build green; 10 pages built in 2.13s.
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
Motion and accessibility
Build green; 10 pages built in 1.90s.
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.
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:
fullstackhero.svg is left in public/ for ad-hoc use even though it's not wired by default.
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.
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
Section-by-section
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.
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)
dotnet runboots the stack.NEW SECTIONS (fact-checked content only)
SEO/GEO INFRASTRUCTURE
H2 + EYEBROW KEYWORD TIGHTENING
META + DOCUMENT
Build green; 10 pages built in 2.18s. robots.txt + llms.txt served correctly on the dev server.
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
CodeFirst (01)
WhatsIncluded (02)
ModuleShowcase (03)
TechStack (04)
FAQ (06)
FinalCta (07)
Build green; 13 pages built in 2.68s.
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.
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.
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.
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.
Brand:
Social proof:
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:
Sidebar now takes a
prefixprop (default 'fsh', mobile passes 'fsh-m') so aria-controls / labelledby IDs stay unique across the two simultaneous renderings.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).
Six concrete improvements aimed at unlocking AI citability and search ranking on top of the editorial landing already in place.
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.
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.
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.
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 AIcrawlers can ingest the entire docs corpus in a single fetch.
Builds 429-line file at /llms-full.txt covering 16 pages.
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)
Cross-link landing → docs (WhatsIncluded + FAQ)
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.
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
matchfield on the nav item — Docs still highlights for any /docs/* path even though its link target is deeper.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.
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)
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
Getting Started SEO frontmatter
Verified
FullStackHero.CLIpackage +fshtool command in src/Tools/CLI/FSH.CLI.csproj,fsh new+fsh doctorcommands in Commands/./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).
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.
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
impersonation + 2FA + sessions + password policy. ~1,800 words.
48 endpoints tabulated. References IdentityModule.cs + Domain/
FshUser.cs + Contracts/v1/Tokens/.
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.
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.
IFileAccessPolicy + finalize state machine + Files Categories
config + IFileScanner hook + orphan/retention purge jobs +
FileFinalizedIntegrationEvent. ~1,300 words.
DirectKey for DMs + tombstone soft-delete + threads + reactions
module-order quirk (Notifications 750 before Chat 800).
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.
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.
Across all ten pages we now document
Build green; 34 pages built in 4.75s (up from 27 last pass).
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
ICurrentUser, CustomException hierarchy
Specification, audit + domain-event
interceptors
FshPermission, claim/action/resource
constants, audit attributes
ModuleLoader, GlobalExceptionHandler,
ValidationBehavior, CurrentUserMiddleware
decorator with OTel; stampede protection
IEventBus, IEventSerializer (dep-free)
EfCoreOutboxStore, EfCoreInboxStore,
OutboxDispatcher
FshJobFilter (tenant context) + dashboard
basic auth + stale-lock cleanup
SendGrid implementations
and S3StorageService + presigned URLs
+ QuotaMeteredStorageService decorator
plan resolution + gauge providers +
enforcement middleware
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).
Synthesizes patterns documented across the module and building-block pages into four conceptual deep-dives at /docs/architecture/.
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.
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.
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.
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).
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;- docs(sections): testing + security + cross-cutting-concerns full pages
- 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.
- 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.
- 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).
- docs(cross-cutting): split into 11 per-topic pages
- caching.mdx (1) HybridCache L1+L2 + stampede + tag eviction
- background-jobs.mdx (2) Hangfire + FshJobFilter (tenant context) +
- observability.mdx (3) Serilog enrichers + OpenTelemetry 1.15 +
- idempotency.mdx (4) Idempotency-Key header + HybridCache replay
- feature-flags.mdx (5) Microsoft.FeatureManagement + TenantFeatureFilter
- rate-limiting.mdx (6) auth policy on 6 endpoints + AuthRateLimitWiringTests
- health-checks.mdx (7) /health and /health/ready + per-DbContext +
- server-sent-events.mdx (8) SSE primitive + browser EventSource client +
- realtime.mdx (9) SignalR over Redis backplane + AppHub group
- http-resilience.mdx (10) Polly v8 standard pipeline + HTTP-layer vs
- error-handling.mdx (11) GlobalExceptionHandler + RFC 9457
- docs(security): split into 8 per-topic pages
- authentication.mdx (1) JWT bearer + rotating refresh + lockout +
- authorization.mdx (2) Permission-based gates, role+group aggregation,
- impersonation.mdx (3) Time-bound grants, IGlobalEntity persistence,
- two-factor.mdx (4) TOTP enrol/verify/disable; recovery-codes
- webhook-signing.mdx (5) HMAC-SHA256 signing scheme + both C# and
- data-protection.mdx (6) Redis-backed key persistence so cookies +
- cors-and-headers.mdx (7) CORS-before-HTTPS-redirect ordering, the
- production-checklist.mdx (8) The 10-item release gate — JWT rotation,
- docs(testing): split into 6 per-topic pages
- unit-tests.mdx (1) xUnit + Shouldly + NSubstitute +
- integration-tests.mdx (2) WebApplicationFactory + Testcontainers
- architecture-tests.mdx (3) 9 rule families + 48 tests; module-boundary
- fixtures-and-seed-data.mdx (4) DatabaseFixture + RedisFixture + MinioFixture
- running-tests.mdx (5) Commands (everything, single project, filter,
- writing-new-tests.mdx (6) Three step-by-step recipes: unit (with full
- docs(frontend + guides): impersonation walkthrough + frontend split + screenshot placeholders
- components/docs/Screenshot.astro — file-exists check at build time; styled dashed-border placeholder when missing, real
+ caption when present. Wired into mdxComponents export.
- 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?).
- admin.mdx (~1,500 words) — operator-facing app: 11 page
- dashboard.mdx (~1,400 words) — tenant-facing app: catalog,
- architecture.mdx (~1,500 words) — shared patterns across both
- theming.mdx (~1,200 words) — per-tenant theming via CSS
- development.mdx (~1,400 words) — Vite npm scripts, Aspire-
- fix(docs): hide screenshot placeholders in production builds
- feat(dashboard+backend): v1 UI polish, file visibility/sharing, permission catalog
- fix(tests): update role helpers to deserialize PagedResponse
- feat(dashboard): A+ rollup — deps + a11y + perf + tokens + UX
- 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).
- 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).
- 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.
- Migrated h1s in EntityPageHeader, PageHero, OverviewPage, NotFoundPage to the new tokens. Chat channel header demoted h1 -> h2 so each route has a single h1.
- 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.
- 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
- 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.
- 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.
- @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.
- 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.
- 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).
- Refreshed two Playwright auth specs (forgot-password, reset-password) that were asserting the old "// 02.FORGOT- PASSWORD" / "// 03.RESET-PASSWORD" eyebrow chrome and
- 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
- 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
- refactor(identity,multitenancy): propagate CancellationToken through service layer + endpoints
!importantplus :where()'s 0-specificity cleanly defeats the prose cascade.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)
security/index.mdx (~2,000 words)
cross-cutting-concerns/index.mdx (~1,800 words)
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).
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
+ shared multiplexer
FshJobActivator (scoped DI) + the 5 cron
jobs the kit ships + retry attributes
instrumentation matrix + MediatorTracing
+ retention window + content-dedupe disclaimer
+ staged rollout / kill switch / per-env patterns
+ adding custom policies + monitoring 429s
Redis + MinIO + TenantMigrationsHealthCheck
+ k8s liveness/readiness probe wiring
?access_token= auth + heartbeat config
conventions (user:{id} + channel:{id}) +
Context.User vs ICurrentUser gotcha +
TestServer-no-WebSocket integration testing
job-layer retry split + circuit breaker +
hedging + non-idempotent retry pitfall
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).
Replaces the single-page security index with one MDX per concern.
The eight new pages
password policy + email confirmation flow
and the silent-no-op gotcha for duplicate
RequiredPermissionAttribute declarations
jti revocation list, cross-tenant root override,
full audit trail
gap flagged as recommended add-on
Node/Express subscriber-side verification
recipes + replay protection via delivery id
antiforgery + email tokens survive rolling deploys
SignalR-credentialed-CORS gotcha, full CSP +
HSTS + X-Frame-Options reference
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).
Replaces the single-page testing index with one MDX per layer plus meta pages (fixtures, running, writing).
The six new pages
AutoFixture; Method_Should_X_When_Y naming;
three ground rules (Shouldly, direct SUT,
no infrastructure); aggregate-invariant +
validator + mock-verification patterns
Postgres/Redis/MinIO; FshWebApplicationFactory
+ Seed.* + CreateAuthenticatedClient helpers;
full ImpersonationTests example
+ endpoint-convention + feature-folder
examples; "specification in XML doc + assertion
in code" pattern; what arch tests don't cover
composition; FshWebApplicationFactory wiring;
Seed.* static accessor; insert-don't-mutate
discipline; common mistakes
verbose, coverage); Docker requirement; full
GitHub Actions workflow; CI sharding pattern;
container cleanup
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).
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
New guides page
Frontend section — 5 sub-pages + slim index
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.
chat (the densest realtime page), files presigned upload, tickets,
identity (profile + sessions + 2FA). 12 screenshot placeholders
covering every major page.
apps: folder layout, provider stack order, apiClient interceptors
(auth + refresh + ProblemDetails), TanStack Query conventions,
RouteGuard, RealtimeProvider, env handling.
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.
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).
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.
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.
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.
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
Dead code
Tokens + visual cohesion
Type scale migration
Fonts
Accessibility (WCAG 2.2 AA)
announceprop, single mount (bell footer) now owns the live region — the chat-rail instance stops duplicate-announcing reconnect events.Forms
Performance
UX
ESLint
Tests
chip rendering that the v1 AuthShell redesign (commit 519d664d) retired. Tests now target the current headline + body text.Docs
Add a Cloudflare D1-backed page view counter to the docs site:
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."
Full-sweep CancellationToken audit (Roslyn navigator AP009). Adds `CancellationToken cancellatio…