Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
4f94fba
Add Components onboarding, recipes, and curated navigation
woksin May 30, 2026
cfd0ec8
Rebuild the Components overview as a landing page
woksin May 30, 2026
a644746
Add a 'choosing a component' decision guide
woksin May 31, 2026
27ef6f6
Rewrite Components getting-started as a guided tour
woksin May 31, 2026
8aa42a8
Add Components tutorial and a Coming from PrimeReact bridge
woksin May 31, 2026
9f70950
Fix DataPage props and enrich the DataPage reference
woksin May 31, 2026
997c92d
Fix import path and complete the data-display recipe examples
woksin May 31, 2026
6531562
Add Cratis docs AI rules and skills (shared from Documentation)
woksin Jun 1, 2026
73887e0
Wire the Storybook embed page; fix the static Storybook build
woksin Jun 4, 2026
1f3ccfd
Merge remote-tracking branch 'origin/main' into docs-overhaul
woksin Jun 4, 2026
5b2a114
Fix incorrect API details in the FilterPanel reference
woksin Jun 4, 2026
839e244
Correct drifted/fabricated APIs in component reference snippets
woksin Jun 4, 2026
5b07aa4
Restore a real root-vs-subpath contrast in the migration guide
woksin Jun 4, 2026
0002821
Add explicit Prerequisites section to Components getting-started
woksin Jun 5, 2026
dc25f33
Use an event-model diagram in the Components tutorial
woksin Jun 5, 2026
3abf5e5
Refine Components getting-started and tutorial pages
woksin Jun 5, 2026
3e2370b
Merge remote-tracking branch 'origin/main' into docs-overhaul
woksin Jun 5, 2026
dd7e3a5
Fix DataPage menu-item icons and ColorPickerField default
woksin Jun 6, 2026
4d76da6
Fix PivotViewer required vs optional props in reference
woksin Jun 6, 2026
82c26eb
Merge remote-tracking branch 'origin/main' into docs-overhaul
woksin Jun 6, 2026
e43abbf
Polish Components documentation for release
woksin Jun 6, 2026
0e51db6
Clean up docs AI guidance
woksin Jun 7, 2026
0d0ce33
Keep docs rendering guidance site-local
woksin Jun 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions .ai/rules/documentation-structure-and-formatting.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
---
applyTo: "**/Documentation/**/*.{md,mdx}"
paths:
- "**/Documentation/**/*.md"
- "**/Documentation/**/*.mdx"
---

# Documentation Structure & Formatting (so a page fits the site)

The mechanical conventions a page must follow to slot into the Astro Starlight site correctly. (For *what* to write — the tour voice — see [Writing Cratis Documentation](./writing-cratis-docs.md). For *where* a page lives — see [Editing Cratis Documentation](./editing-cratis-docs.md).) Product `.md` is run through `web/scripts/sync-content.mjs` before it reaches Starlight; this is what that converter expects and does.

## Frontmatter

```yaml
---
title: Append an event # required-ish; becomes the page H1
description: One sentence … # strongly recommended; SEO/meta + AI export
sidebar: # optional: order / label / badge
order: 2
badge: { text: New, variant: tip }
---
```

- The converter **keeps** `title`, `description`, `sidebar`; it **drops** DocFX keys (`uid`, `applyTo`, `storybook`, …). For product `.md` with no frontmatter, it derives a `title` from the first H1.
- **Starlight renders `title` as the page H1 — so do NOT put an H1 in the body.** Start the body at `##` (H2). A leading body H1 is stripped, but don't rely on that; structure from H2.

## Headings

- **No body H1** (the title is the H1).
- **The "On this page" ToC shows H2 only** (`tableOfContents: { min:2, max:2 }`). Structure each page as a flat list of `##` sections — sub-points go under them as `###`/prose, not as ToC entries.
- Use sentence case in headings; no trailing punctuation.

## File & folder layout

- Every folder has a **`toc.yml`** (navigation) and an **`index.md`** (landing). A folder whose landing is `overview.md` (no `index.md`) **404s on the bare URL** — give it an `index.md` or link to a specific page.
- A `toc.yml` entry pointing to a **missing page is dropped** from the sidebar (the sync prints "N broken toc entries dropped" — keep it 0).
- `toc.yml` sections are regrouped into Diátaxis **buckets** (Get started / Guides / Understand / Reference) via `PRODUCTS[].buckets` in `sync-content.mjs`. Sections named **"Getting started…"** auto-get a *Quickstart* badge; **"Tutorial"** gets a *Tutorial* badge.

## Callouts / asides

Write DocFX alerts in product `.md` (converted automatically) **or** Starlight directives directly:

| DocFX | Becomes |
|---|---|
| `> [!NOTE]` / `> [!IMPORTANT]` | `:::note` |
| `> [!TIP]` | `:::tip` |
| `> [!WARNING]` | `:::caution` |
| `> [!CAUTION]` | `:::danger` |

```
:::note
Body of the callout.
:::
```

In `.mdx` you may also use `<Aside type="tip">…</Aside>` (import from `@astrojs/starlight/components`).

## Code blocks

- **Always tag the language** for highlighting: ` ```csharp `, ` ```tsx `, ` ```bash `, ` ```yaml `, etc. DocFX-era custom fences (`env`, `pdl`, `ebnf`, `pql`, `gitignore`, `flow`) are aliased to plain text so they don't warn — but use a real language where one exists.
- **No spurious common leading indentation** — if a snippet is lifted from inside a method, dedent it to column 0 (the whole block shifted right reads as "indented"). Keep only the code's own internal indentation.
- Where a feature spans the stack, show **both sides** with `<FullStackTabs>` (synced C# ↔ generated TS) rather than backend-only.
- `[!INCLUDE [x](./x.md)]` is inlined; `[Snippet source](…)` links stay as-is.

## Tables, images, links, diagrams

- **Tables** are GitHub-Flavored Markdown (`| … |` with a `| --- |` separator row, blank line before). They render as real tables (the site tints the header). A table that shows as raw `|` pipes means GFM isn't applying — check the Documentation repo's site-rendering rule.
- **Images** use relative paths next to the page; they're click-to-zoom automatically. Give meaningful alt text.
- **Links:** product `.md` may use `./foo.md`; links to a `.mdx` page must be **extension-less** (`./foo`); site-level `.mdx` uses clean root-relative URLs (`/arc/…`). Cross-product links are root-relative. **Link text must describe the destination** — never `[here]` / `[see documentation]` (the gate fails on these).
- **Diagrams:** ` ```mermaid ` fences for any non-trivial concept (pre-rendered to SVG at build).

## `.mdx` specifics

- Frontmatter `title` + `description`, **no body H1**, then imports:
```mdx
import { Steps, Tabs, TabItem, Aside } from '@astrojs/starlight/components';
import { FullStackTabs, TopicHero, SimpleCard, StackDiagram } from '@components';
```
- Valid `<TabItem>` icons include `apple`, `linux`, `seti:c-sharp`, `seti:react`.
- The splash front door uses `template: splash` in frontmatter.

## File hygiene

- **End every file with a single trailing newline.**
- Don't use shell commands to modify a doc after writing it.
- Verify before done: `cd Documentation/web && npm run check` must end **0 error(s)** and **0 broken** links.
54 changes: 54 additions & 0 deletions .ai/rules/editing-cratis-docs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
---
applyTo: "**/Documentation/**/*.{md,mdx}"
paths:
- "**/Documentation/**/*.md"
- "**/Documentation/**/*.mdx"
---

# Editing Cratis Documentation (the content is split across repos)

Cratis documentation is **not** one folder. The content lives in **each product's own repo** under that repo's `Documentation/` folder, and a single published site (in the **`Documentation` repo**, at `Documentation/web/`) aggregates them all at build time. Know where a page actually lives before you edit it, or you'll edit a generated copy that gets overwritten.

## Where each page actually lives

| Page | Source of truth |
|---|---|
| `/chronicle/**` | `Chronicle/Documentation/**` |
| `/arc/**` | `Arc/Documentation/**` (the `ApplicationModel` repo, cloned as `Arc`) |
| `/components/**` | `Components/Documentation/**` |
| `/cli/**`, `/fundamentals/**`, `/contributing/**` | the matching repo's `Documentation/` |
| Site-level pages (`/`, `/why-cratis`, `/cratis-stack`, `/glossary`, `/comparisons/**`, …) | `Documentation/web/src/content/docs/*.{md,mdx}` (authored directly in the site) |

**Map a URL to a file:** `/chronicle/concepts/event-source/` → `Chronicle/Documentation/concepts/event-source.md`. The site reads each product repo as a **sibling clone** (`<parent>/{Documentation,Chronicle,Arc,Components,Fundamentals,cli,.github}`), all on the same branch.

## The one rule that prevents wasted work

**NEVER edit `Documentation/web/src/content/docs/{chronicle,arc,components,cli,fundamentals,contributing}/`.** Those are **generated and git-ignored** — `web/scripts/sync-content.mjs` regenerates them from the product repos on every build. Edit the **product-repo source** and re-sync. (Site-level pages directly under `web/src/content/docs/` *are* hand-authored — only the per-product subfolders are generated.)

## The loop

1. **Edit** the source `.md`/`.mdx` in the owning product repo (or the site-level page in `web/src/content/docs/`).
2. **Sync + preview:** from `Documentation/web`, `npm run dev` (runs the sync, serves http://localhost:4321). The sync also runs automatically in `predev`/`prebuild`.
3. **Verify:** `npm run check` — the full gate. It MUST end **0 error(s)** and **0 broken** links (≈187 advisory style warnings are expected and fine). Run it after every change.
4. Commit the change in the **product repo** (content) — the site repo only changes if you touched site-level pages, nav buckets, or the build.

## Adding or moving a page

- Create the file in the product repo's `Documentation/`, add a `toc.yml` entry, and add that entry's `name` to the right Diátaxis **bucket** in `web/scripts/sync-content.mjs` (`PRODUCTS[].buckets`). Site-level pages are wired in `astro.config.mjs` instead.
- Buckets are the Diátaxis order: **Get started** (tutorials) → **Guides** (how-to) → **Understand** (explanation) → **Reference**. Keep every product on this shape.

## Links (the rules differ by file type)

- **Product `.md`** goes through the converter — `[x](./foo.md)` is fine (it strips `.md`).
- **Intra-doc links to a `.mdx` page must be EXTENSION-LESS** (`./validation`, not `./validation.mdx`) — slugify strips the dot and breaks `.mdx`.
- **Site-level `.mdx`** does NOT go through the converter — use clean root-relative URLs (`/arc/...`), never `.md`.
- Cross-product links are root-relative: `/chronicle/...`. Sections whose landing is `overview.md` (no `index.md`) 404 on the bare URL — link to a specific page.

## Hard gotchas (these will bite you)

- **A long-running `npm run dev` degrades** (every page 500s / tables stop rendering) — and **running `npm run check`/`build` while dev is live** corrupts it (the build re-syncs content the dev server is watching). Fix: **restart `npm run dev`** (kill `lsof -ti tcp:4321`). Re-verify a fresh dev server before trusting any "X doesn't render".
- **The Astro content cache (`.astro/`, `node_modules/.astro`) silently serves a PARTIAL prior render** between iterations. If a change "didn't take" or a build looks half-done, `rm -rf .astro node_modules/.astro` and rebuild.
- **Code examples are copied verbatim by readers** — verify every framework API against real source before writing it. See [Writing Correct Code Examples](./writing-correct-examples.md).
- Match the **tour voice** (teach, don't dump) and the right **Diátaxis** type — see [Writing Cratis Documentation](./writing-cratis-docs.md).

→ Site build and rendering internals live in the Documentation repo.
35 changes: 35 additions & 0 deletions .ai/rules/writing-correct-examples.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
applyTo: "**/Documentation/**/*.{md,mdx}"
paths:
- "**/Documentation/**/*.md"
- "**/Documentation/**/*.mdx"
---

# Writing Correct Code Examples (Technical Docs)

Documentation code examples are **copied verbatim** by evaluators. A snippet that uses an API that doesn't exist is worse than no snippet — it breaks on first paste and loses trust. An audit of these docs found **~12 fabricated-API bugs** that had passed review and shipped. The discipline below is how you avoid adding the thirteenth.

## The rule: verify every framework API against real source — before you write it

For each framework type, attribute, method, prop, hook, or import in an example, confirm it exists and has that exact shape in **source**, not in another doc page (the docs themselves had the bugs):

- **C# / backend** — grep real usage in **Studio** (`/Volumes/sourcecode/repos/cratis/Studio/Source`, `*.cs`) and the product `Source/` trees (`Arc/Source`, `Chronicle/Source`). For extension methods, find the `public static … (this <Type> …)` signature and note **which type it extends**.
- **React / Components** — the authoritative prop names are in the compiled type defs: `Components/Source/dist/esm/**/*.d.ts`. Real usage: Studio `*.tsx`.
- **Invented *domain* names are fine** (event/concept/command names like `AuthorRegistered`, `BookId`). Only **framework APIs** must be real. Never invent a framework interface, attribute, prop, method, or import path.

## Complete and correct

- No pseudo-code, no `// ...` elisions that leave the reader guessing, no props/members that don't exist.
- A snippet a reader pastes should compile (modulo the invented domain types they'd supply).

## Verified gotchas (the real APIs — these are the ones docs kept getting wrong)

- Commands/queries are **model-bound**: a `[Command]` record with `Handle()` **on the record**, and `[ReadModel]` records with **static** query methods. The marker/handler interfaces `ICommand`, `ICommandHandler<T>`, `IQuery<T>`, `IQueryHandler<T,R>` **do not exist** — never use them.
- Bootstrap: `ArcApplication.CreateBuilder(args)` (not `ArcApplicationBuilder.CreateBuilder`). `builder.AddCratisArc()` on the builder (`WebApplicationBuilder`/`IHostBuilder`); `app.UseCratisArc()` on the built app and it takes **no args** (the listen URL comes from `ArcOptions.Hosting.ApplicationUrl`).
- Read the current user inside `Handle()` by injecting **`IHttpContextAccessor`** and reading `HttpContext?.User` (`ClaimsPrincipal`). There is no `CommandContext.User` and no `IUserAccessor` Arc type. In-`Handle` guards return `Result<ValidationResult, T>` + `ValidationResult.Error(...)` — there is no `CommandResult.Forbidden`/`Unauthorized` to return.
- Components: `DataPage` uses the **compound** `DataPage.Columns` / `DataPage.MenuItems`; the detail prop is **`detailsComponent`** (lowercase) — `detailsTitle`/`initialSizes` are **not** props. Import `DataTableForObservableQuery` from `@cratis/components/DataTables` (the root barrel only re-exports namespaces); `DataPage`/`MenuItem` from `@cratis/components/DataPage`. Required props like `emptyMessage`/`title` must be present.
- Chronicle model-bound projections use property attributes **`[SetFrom<T>]`** / **`[SetValue<T>]`** (and `[FromEvent<T>]` AutoMap) — **not** `static On(event)` methods (that shape doesn't exist). Retrieve a read model with `eventStore.ReadModels.GetInstanceById<T>(id)`. Integration assertion is `ShouldHaveAppendedEvent<TEvent>(seq, eventSourceId, validator)` (**3 args**).

## Auditing at scale

Re-run a snippet-correctness audit periodically — it keeps finding bugs (the list above came from three rounds). Delegate the cross-checking to subagents that compare each snippet to source and report only confirmed discrepancies; **verify each finding against source yourself before fixing**. Consider adopting an automated example tester (**Doc Detective**, **Squidler** — from awesome-docs) that actually runs the snippets, so correctness is enforced by CI rather than by hand. The principle, from jvns's "write good examples by starting with real code": derive examples from working source, don't compose them from memory.
75 changes: 75 additions & 0 deletions .ai/rules/writing-cratis-docs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
---
applyTo: "**/Documentation/**/*.{md,mdx}"
paths:
- "**/Documentation/**/*.md"
- "**/Documentation/**/*.mdx"
---

# Writing Cratis Documentation — tour voice + Starlight authoring

The Cratis docs must **take the reader on a tour, like a teacher** — the way [Marten](https://martendb.io), [Wolverine](https://wolverinefx.net), and [aspire.dev](https://aspire.dev) docs do — **not** state facts like a reference dump. This is the project's strongest qualitative bar and it's been flagged repeatedly. The differentiator of those docs is **pedagogical structure**, not looks. Match it.

## The bar

- **Pain → relief.** Open by naming the friction the reader feels, then reveal the feature as the relief. (Aspire's "without vs. with".)
- **Why before how.** A reader who understands the reasoning handles edge cases the docs don't cover.
- **Active voice, present tense, second person.** "You append the event," not "the event is appended."
- **Be honest about limits.** A "when this is the wrong fit" section builds more trust than omitting it.

## One page = one Diátaxis type (it also picks the nav bucket)

| Type | Reader is… | Reads like | Nav bucket |
|---|---|---|---|
| **Tutorial** | learning by doing | a guided lesson — each step a visible result | Get started |
| **How-to** | solving a specific problem | a recipe — assume competence, no teaching | Guides |
| **Explanation** | trying to understand | a discussion — concepts, trade-offs, *why*, a diagram | Understand |
| **Reference** | looking something up | a dictionary — exhaustive, terse, tables/signatures | Reference |

Never mix types. A tutorial padded with reference detail overwhelms; a how-to interrupted by concept digressions stops being a recipe.

## The tour-voice checklist (apply to tutorials, getting-started, and explanations)

1. **Open with a concrete scenario** ("a book shows up — let's record that"), not a definition of the tool.
2. **Name the friction first**, then the feature as its relief.
3. **"Let's…" with chronological verbs** (define → append → project → query).
4. **After every code block, explain the invisible** — what happens under the hood and why it matters.
5. **Recap before pivoting** ("this works well for X… however…").
6. **Anticipate the reader's doubt** with an inline aside ("in a real app you'd…").
7. **Show the result** — the output, the resulting read model/JSON — so success is visible.
8. **Organize by workflow**, not alphabetically (especially CLI/command docs).
9. **End every section with a forward link** to the natural next step.

`Chronicle/Documentation/tutorial/*` is the reference voice — read it before writing.

## Achieve the tour voice with Starlight's authoring tools

Author rich pages as **`.mdx`** (the converter keeps `.mdx`): frontmatter `title` + `description`, **no body H1**, then import what you need:

```mdx
import { Steps, Tabs, TabItem, Aside } from '@astrojs/starlight/components';
import { FullStackTabs, TopicHero, SimpleCard, StackDiagram, YouWillLearn, Recap } from '@components';
```

- **`<Steps>`** — procedures where each step produces a visible result (checklist point 1, 3).
- **`<Tabs>` / `<TabItem>`** — alternatives (OS, console-vs-web, C#-vs-fluent). Valid icons include `apple`, `linux`, `seti:c-sharp`, `seti:react`.
- **`<Aside>`** — anticipate doubt / call out a gotcha (checklist point 6).
- **`<FullStackTabs>`** — synced C# ↔ generated-TypeScript tabs. **This is the differentiator** (full-stack type safety) — use it wherever a feature spans the stack; show both sides, not just the backend.
- **`<TopicHero>` + `<SimpleCard>`** — the per-product Overview landings (pain→relief framing + a card grid).
- **`<StackDiagram>`, `<YouWillLearn>`, `<Recap>`** — the stack picture, a chapter's learning goals, and the closing recap (checklist points 1 and 5).
- **A Mermaid diagram for every non-trivial concept** — architecture, event/command flow, state transitions. A concept page without a diagram is usually incomplete. (The Documentation site pre-renders them to SVG at build time.)

## Two voices, and connect the products

- **Two voices per area:** the toured/educational layer (tutorial, getting-started, "Understanding…") **and** the terse, exhaustive reference. The narrative pages **link *down*** into the reference; the reference stays a dictionary.
- **Connect at the seams** rather than re-explaining: a command (Arc) appends events (Chronicle); a query reads a projection (Chronicle) rendered by a component (Components). Link to the **glossary** for shared terms instead of redefining them.
- **Coming-from-X bridges** map new concepts to what the reader knows (MediatR, MVC, EF/CRUD, Marten/other event stores).

## Before you call a page done

- Every framework API in a code example is **verified against real source** — see [Writing Correct Code Examples](./writing-correct-examples.md). Readers paste snippets verbatim.
- `npm run check` is green (0 errors · 0 broken links); the page sits in the right nav bucket.
- For a visual page, screenshot it in light **and** dark — see the `qa-cratis-docs` skill.

**Study the masters (cloned locally):** `~/src/repos/aspire.dev` (Starlight — great CLI docs + tour writing), `~/src/repos/marten`, `~/src/repos/wolverine`. Mine their *patterns*; never copy their text.

→ The mechanical format a page must follow (frontmatter, headings, asides, code fences, file layout): [Documentation Structure & Formatting](./documentation-structure-and-formatting.md). The edit→sync→verify loop and where pages live: [Editing Cratis Documentation](./editing-cratis-docs.md). Site build internals live in the Documentation repo.
Loading
Loading