feat(skills): move Write the docs skills into the monorepo — DO NOT REVIEW YET (#48914)

## I have read the CONTRIBUTING.md file.

YES

## What kind of change does this PR introduce?

Adds four AI agent skills that support docs contributors across the
authoring lifecycle, intended to lower the barrier to entry for
contributing to our docs.

Closes DOCS-1287.

## What is the current behavior?

Our process for writing docs is somewhat undefined beyond some general
guidance in CONTRIBUTING.md and we don't make as easy to contribute to
our docs as we could. As a result, content often needs additional
changes during PR reviews or requires further revisions after merging.

The four AI agent skills in this PR already existed in a private repo
where I've been testing them but they were not previously available for
general use until now.

## What is the new behavior?

- Four skills added under `.agents/skills/`, symlinked from
`.claude/skills/` and `.cursor/skills/` (same pattern as the existing
`vitest` skill).
- `ask-the-docs`: answers architecture and design questions about
apps/docs (MDX pipeline, content components, federated docs) and checks
whether a proposed change fits existing docs app patterns.
- `write-the-docs`: drafts net-new or substantially rewritten docs
content for a feature or launch, grounded in the Linear ticket, the
actual code, and the docs style guide.
- `review-the-docs`: runs a local, PR-type-specific review checklist
against any open supabase/supabase docs PR (markdown pipeline, MDX
content, tutorials, examples, Studio links) and produces a consolidated
report.
- `pm-the-docs`: supports "Write the docs" authoring process across the
different phases.
- `apps/docs/CONTRIBUTING.md` gets a new "AI agent skills for docs
authoring" section mapping each skill to its checklist stage
- Cross-references to skills that stay in `docs-agent-skills`
(`work-linear-issue`, `audit-docs-ia`, `create-pull-request`,
`proof-it-works`, `pm-the-docs-full`) now point there via absolute
GitHub links instead of relative paths

## Additional context

- Companion PR:
[supabase/docs-agent-skills#28](https://github.com/supabase/docs-agent-skills/pull/28).
Removes the three moved skills, renames `pm-the-docs` to
`pm-the-docs-full`, and fixes now-dangling inbound links.
- Worktree:
`~/GitHub/supabase/supabase-worktrees/nikrichers/docs-1287-move-skills-mentioned-in-write-the-docs-from-docs-agent`
- Opened as draft: this is a docs-authoring-tooling change with no
runtime/build surface. Flip to ready once you've sanity-checked the
skill content.

### Test plan

- [ ] `ls -la
.claude/skills/{ask-the-docs,pm-the-docs,write-the-docs,review-the-docs}`
resolves to `.agents/skills/...`
- [ ] Open a fresh Claude Code session with cwd in this repo and confirm
`/ask-the-docs`, `/pm-the-docs`, `/write-the-docs`, `/review-the-docs`
are available
- [ ] Read the new section in
[`apps/docs/CONTRIBUTING.md`](apps/docs/CONTRIBUTING.md) in context
- [ ] Spot-check
`.agents/skills/pm-the-docs/reference/write-the-docs-checklist.md` has
no `linear.app` links and carries the snapshot disclaimer

---------

Co-authored-by: Nik Richers <nik@validmind.ai>
This commit is contained in:
Nik RichersandNik Richers authored and GitHub committed 2026-08-12 22:48:30 +00:00
1 parent 8baaa517d0
commit cf36ad9e52
27 files changed
+2494

No files matched your search

+134
View File
@@ -0,0 +1,134 @@
---
name: ask-the-docs
description: >-
Answer questions about the Supabase docs app (apps/docs) using
documented architecture, build pipeline, and review-pattern notes, and
apply feature-design principles (codebase reuse, coding minimalism)
when proposing or critiquing changes. Use when the user asks "how does
X work in the docs app?", "where does Y live?", "is this approach OK
for the docs app?", or before writing non-trivial changes under
apps/docs/ — especially anything touching the MDX pipeline, markdown
generation, content components, federated docs, or contributor-facing
authoring patterns. Can answer architecture questions with Mermaid
diagrams when helpful.
---
# Ask the docs-app librarian
A reference for `apps/docs` knowledge — architecture, build pipeline,
federated docs, known fragilities — plus the feature-design principles
the codebase rewards: **understand and reuse the existing code before
writing new code**, and **practice coding minimalism** to keep the
surface area small.
Two jobs:
1. **Look up what's already documented** about the docs app —
architecture, tradeoffs, gotchas, prior decisions — instead of
re-deriving from cold reads.
2. **Pre-empt review feedback** by applying the codebase-reuse /
minimalism principles before opening a PR. Catches the "fix it in the
next round" comments early.
## When to invoke
- User asks about `apps/docs` architecture, conventions, or behavior
("how does the markdown pipeline work?", "where do listings data files
go?", "why does Troubleshooting have a `.mjs` utils file?").
- User asks about LLM/agent consumption (`llms.txt`, markdown negotiation,
`searchDocs`, bulk exports, agent onboarding guides, humans vs agents vs
crawlers, AI prompt blocks in quickstarts).
- About to write code under `apps/docs/` that touches: MDX components,
`internals/markdown-schema/`, `generate-guides-markdown.ts`, content
data modules, the lint pipeline, telemetry events, contributor-facing
snippets, federated routes, reference codegen, or Management API /
OpenAPI reference pages.
- Reviewing a docs-app PR and want a sanity check against the documented
principles.
**Not for:** general Supabase docs _content_ questions (use
`work-linear-issue`, `audit-quickstarts`, etc.), or app-level work outside
`apps/docs/`.
## Answering with diagrams
Architecture and pipeline questions are often clearer with a diagram
than with prose. Default to including a **Mermaid diagram** in answers
about:
- The MDX runtime vs markdown-export pipeline split.
- Build flow (Turbo → pnpm `prebuild` / `build` / `postbuild` → Vercel).
- LLM/agent consumption surface (`llms.txt`, negotiation, bulk exports).
- Federated docs fetch flow.
- CI / PR flow.
- Component / data-registry relationships.
- Management API OpenAPI → codegen → reference page flow.
Mermaid fences (`` ```mermaid `````) render natively on GitHub, Cursor,
and most Markdown previewers. Several reference files already embed
Mermaid; reuse or adapt them rather than re-deriving.
Keep diagrams **small and one-topic**. If a diagram needs more than a
dozen nodes, split it.
## Reference files
Short, focused docs under `reference/`. Read whichever apply to the task
at hand — they cite each other where context matters.
| File | What's inside |
| ---------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`reference/adding-features.md`](./reference/adding-features.md) | Best-practices guidance for adding features to `apps/docs`. Inventory existing code first, pick the smallest viable shape, reuse pipelines. |
| [`reference/docs-app-direction.md`](./reference/docs-app-direction.md) | Refactoring vision and working norms — what new work should align with. |
| [`reference/known-issues.md`](./reference/known-issues.md) | Living list of broken, fragile, or in-flux systems. Check before depending on anything (federated docs, search, Sentry, reference-page architecture). |
| [`reference/app-map.md`](./reference/app-map.md) | Architecture cheat sheet — directories, the two-pipeline (MDX runtime + markdown export) model, heading/typography contract, telemetry, lint entries. |
| [`reference/build-pipeline.md`](./reference/build-pipeline.md) | Turborepo + pnpm lifecycle steps for building `apps/docs` — codegen, prebuild, postbuild, Vercel deploy. Mermaid diagram included. |
| [`reference/llm-agent-surface.md`](./reference/llm-agent-surface.md) | Audience routing, `llms.txt`, content negotiation, bulk exports. |
| [`reference/llm-agent-parity.md`](./reference/llm-agent-parity.md) | HTML↔markdown fidelity (e.g. AI prompts), search caveat, agent onboarding guides, in-flux wiring. |
| [`reference/federated-docs.md`](./reference/federated-docs.md) | How docs pulls markdown from external repos at build time. Routes, `pageMap`, remark/rehype plugins, link transforms, known failure modes. |
| [`reference/ci-and-lint.md`](./reference/ci-and-lint.md) | GitHub Actions on every PR — `docs_lint`, `Docs Tests`, typecheck, prettier, Vercel preview gate. Where to add a check before creating a new one. |
| [`reference/management-api-reference.md`](./reference/management-api-reference.md) | Management API OpenAPI download → Redocly bundle → codegen → `ApiEndpointSection`; why not to swap in Scalar/Redoc. |
| [`reference/gotchas.md`](./reference/gotchas.md) | Specific traps to watch for. One-liner per item. |
## How to use during a chat
1. **Start by reading** `adding-features.md` and `app-map.md` if the
question touches design choices or unfamiliar code paths. They're
small on purpose — read both, don't skim.
2. **Verify before recommending.** Reference content may lag behind the
live code. Confirm with the actual files (`apps/docs/...`) before
acting on remembered claims about file paths, function names, or
behavior.
3. **Cite the principle**, not just the rule. "Per `adding-features.md`
§ 'Reuse pipelines, don't fork them', this routes through the
existing markdown-schema handler rather than introducing a side
path."
4. **Reach for Mermaid** when explaining architecture, flows, or
relationships — see [Answering with diagrams](#answering-with-diagrams).
## Updating the librarian
This skill lives in `.agents/skills/ask-the-docs/` in `supabase/supabase`.
When something in `apps/docs` changes in a way that makes a reference
file inaccurate, or a generally-applicable lesson emerges from a PR
review, open a pull request against this repo to update the relevant
file, same as any other in-repo change.
Keep each canonical file under ~250 lines; split before they bloat.
Capture only what a future contributor would benefit from knowing — if
a fact is already obvious from a quick read of the live code, don't
write it down.
## Related skills
- [`pm-the-docs`](../pm-the-docs/SKILL.md) — audience, stage, and
cross-cutting scope calls (Frame stage of the "Write the docs" checklist,
mirrored in `pm-the-docs`'s reference file).
- [`work-linear-issue`](https://github.com/supabase/docs-agent-skills/blob/main/.claude/skills/work-linear-issue/SKILL.md) — implementing
assigned DOCS-\* tickets.
- [`review-the-docs`](../review-the-docs/SKILL.md) — reviewing open docs
PRs with type-specific verification.
- [`audit-content-listings`](https://github.com/supabase/docs-agent-skills/blob/main/.claude/skills/audit-content-listings/SKILL.md) — batch
conversion of overview pages to content listings.
- [`create-pull-request`](https://github.com/supabase/docs-agent-skills/blob/main/.claude/skills/create-pull-request/SKILL.md) — opening or
updating a docs PR.
@@ -0,0 +1,206 @@
# Adding features to `apps/docs`
Best-practices guidance for adding features to the docs app. Read this
**before** writing code — most "fix it in the next round" review comments
trace back to skipping one of these steps.
The premise: `apps/docs` has accumulated significant surface area already.
Any new file, build step, lint job, or content shape is a permanent
maintenance cost. The goal is to deliver the feature with the smallest
durable footprint by **understanding the existing code first** and
**reusing what's already there**.
## The cost lens
Every change adds one of two things:
- **Reach** — the feature now does more (user-visible value).
- **Surface** — there is now more code, configuration, or vocabulary to
maintain (recurring cost).
A good change maximizes reach per unit of surface. When a design discussion
stalls, re-frame as: _"Does the user-visible improvement justify the
maintenance cost?"_ If you cannot answer yes confidently, cut scope before
defending the design.
See [`docs-app-direction.md`](./docs-app-direction.md) for the broader
context — the docs app already carries known tech debt, and the maintainer's
stated direction is to reduce surface, not extend it.
## Step 1 — Inventory before you write
Before adding a file, search for what's already there. The docs app has
existing systems for almost every common job; using them is faster than
building parallel ones.
| Need | Look first at |
| ------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| Rendering MDX with a custom component | The MDX component map in `features/docs/MdxBase.shared.tsx` |
| Markdown export of a component | The schema registry in `internals/generate-guides-markdown.ts` and handlers in `internals/markdown-schema/` |
| Reusable content blocks | `<$Partial path="..." />` and `content/_partials/` |
| Headings / typography | `<Heading>` from `MdxBase.shared.tsx`; prose / `not-prose` classes |
| Visual primitives (cards, panels, callouts) | `ui-patterns/GlassPanel`, `ui-patterns/IconPanel`, `ui/Admonition`, etc. |
| Internal vs external link logic | `withDocsBasePath` / `addBaseUrlPrefix` in `lib/internal-links.ts` |
| Telemetry | `useSendTelemetryEvent` + `packages/common/telemetry-constants.ts` |
| Validation / schemas | `zod` schemas under `apps/docs/lib/` |
| Code samples in MDX | `$CodeSample` directive |
| Build steps | `prebuild` / `postbuild` chain in `apps/docs/package.json` |
| CI checks | Existing workflows under `.github/workflows/`. See [`ci-and-lint.md`](./ci-and-lint.md). |
| Lint rules for MDX content | `supa-mdx-lint` configuration — extend it, don't add a new lint job |
If something close to what you need already exists, **the default is to
extend it**, not to build alongside.
## Step 2 — Pick the smallest viable shape
For most feature requests, the shapes in descending order of preference are:
1. **Pure content change** — MDX edit, partial, or data file. No new code.
2. **Configuration of an existing component** — pass a new prop to an
existing primitive; extend a config object.
3. **A new data shape consumed by existing components** — a typed
`*.data.ts` module read by an already-registered MDX component.
4. **A thin component that composes existing primitives** — a small file
that orchestrates `<Link>`, `<GlassPanel>`, `<Heading>`, etc. Adds an
MDX component-map entry but no new visual primitives.
5. **A new primitive in the design system** — last resort. Justify against
`packages/ui` / `ui-patterns`.
Move down the list only when the option above genuinely cannot express the
feature. The further down you go, the more you should write down why.
## Step 3 — Reuse pipelines, don't fork them
If a feature has to render in more than one place (HTML + markdown export,
runtime + build, etc.), wire both consumers through a **single shared
shape** — a data registry, a schema, a constant map — instead of
maintaining parallel implementations.
Pattern that works well in this codebase:
- The MDX component reads from a data registry keyed by an `id`.
- The markdown-export handler reads from the _same_ registry, using the
same `id` carried as a JSX prop.
- The data shape (zod schema) is the single source of truth.
Antipatterns to avoid:
- Two extraction paths that serialize the same content differently.
- A bespoke link-wrapper component when `<Link>` + `<GlassPanel>` already
covers the pattern — compose at the call site instead.
- New custom build steps that run alongside the existing `prebuild` /
`postbuild` chain when a hook already exists.
- A new CI workflow when `docs_lint`, `Docs Tests`, or the existing
typecheck/prettier jobs could absorb the check. See
[`ci-and-lint.md`](./ci-and-lint.md).
- A new content vocabulary (custom front-matter block, novel MDX directive,
new YAML schema) when a React component + partial would express the same
thing.
## Step 4 — Conventions that keep the diff small
These are the patterns most often called out in PR review. None of them
matter individually; together they keep the surface tight.
### File naming
A file's name matches what it exports. If the file exports `Foo`, it's
`Foo.ts(x)`. The directory listing should answer "what's in here?" without
opening the file. Same for handler files in `internals/markdown-schema/` —
the file name is the JSX element name.
### Import aliases
`import { Foo as Bar }` is reserved for genuine name collisions. Aliasing
for "clarity" or "consistency with old naming" adds friction.
### Single-use helpers stay inline
A helper used in one place lives in that place. New files are for shared
code. Wandering helpers in unrelated folders make code hard to find.
### Pure helpers live in `*.utils.ts`
Schema files hold schemas. Data / constant files hold data. Helpers —
including lookups like `getXById` over a constant map — live in
`X.utils.ts`. Predictable location beats "logical grouping by concept."
### Don't override the design system
Use shared primitives (`<Heading>`, `<GlassPanel>`, prose classes) and let
them carry typography and spacing. Adding `text-xl` or `font-semibold` to
a new component is the wrong escape hatch.
### Keep `internals/` out of client and MDX code
`apps/docs/internals/` is for build-time markdown generation. Client
components and the MDX runtime should not import from it. If a function is
needed on both sides, it belongs in `lib/`.
### Markup follows semantics, not visuals
A collection of links is a `<ul>`, regardless of whether it visually
renders as a list or a grid. CSS handles layout; markup handles semantics.
### Collapse near-duplicates with discriminator props
When two components share structure and differ only in classes or markup
details, collapse them. Extract the differences into class-name constants
keyed by the discriminator (`type: 'grid' | 'list'`). Don't ship two
components that are 90% identical.
### Use maps / lookups over arithmetic on known sets
If the input domain is `'h2' | 'h3' | 'h4'`, a `Map` (or `Record<Literal, T>`)
keyed by those literals is clearer than slicing strings and synthesizing
output. Exhaustiveness checking comes along for free.
### Avoid render-time closure churn
Hooks should return _stable_ callbacks (`useCallback`) that consumers
invoke, not curried builders that create a new closure per item per render.
Inline arrow functions at the call site are fine; building closures in a
loop during render is not.
### Be robust on URL / string parsing
`/^https?:\/\//i` is incomplete. Protocol-relative (`//host`), `mailto:`,
`tel:`, and bare schemes are all external. Prefer `new URL(href, base)` or
a regex that covers `^(?:[a-z][a-z0-9+\-.]*:|\/\/)`.
### Keep diffs minimal
Import reordering, prettier reflows, or unrelated whitespace edits in a
feature PR muddy the review. If a file isn't conceptually part of the
change, revert it. Auto-formatter ran on a file you didn't touch? Reset
it.
## Step 5 — Pre-flight before opening the PR
Run through this checklist before pushing:
- [ ] Searched the inventory in Step 1 for existing primitives / pipelines.
- [ ] Picked the smallest shape that delivers the feature (Step 2).
- [ ] Renders consistently across pipelines that share content (Step 3).
- [ ] File and export names match (Step 4 — file naming).
- [ ] No unnecessary helpers, imports, files, or aliases.
- [ ] Typography defers to shared primitives.
- [ ] No `internals/` imports from client / MDX code.
- [ ] Diff contains only changes relevant to the feature.
- [ ] `pnpm format`, `pnpm typecheck`, and the relevant lint job pass.
Replies on review threads move faster when they point to a specific commit
("done in abc1234") than when they explain the reasoning at length.
Expect review to come in **passes** that go progressively deeper — that is
the path to a minimum-surface design, not nitpicking.
## Related
- [`docs-app-direction.md`](./docs-app-direction.md) — broader context on
the docs app's tech debt and refactoring direction.
- [`app-map.md`](./app-map.md) — where each existing seam lives.
- [`build-pipeline.md`](./build-pipeline.md) — the build steps you should
consider extending before adding a new one.
- [`ci-and-lint.md`](./ci-and-lint.md) — the CI checks you should consider
extending before adding a new workflow.
- [`gotchas.md`](./gotchas.md) — specific traps known to bite.
@@ -0,0 +1,242 @@
# `apps/docs` architecture map
What lives where, and what depends on what. **Verify against the live tree
before acting** — paths drift. Cross-references the per-topic deep dives in
this folder.
## High-level architecture
`apps/docs` is a **Next.js 15 App Router** site served at **`/docs`**
(`basePath` in `next.config.mjs`). It combines hand-written MDX guides,
machine-generated reference docs, troubleshooting content, federated content
from external repos, and shared monorepo packages (`ui`, `common`,
`ui-patterns`, etc.).
```mermaid
flowchart TB
subgraph routes ["app/ — Next.js routes"]
guides["/guides/*"]
reference["/reference/*"]
api["/api/*"]
end
subgraph content ["Content sources"]
mdx["content/guides/*.mdx"]
trouble["content/troubleshooting/*.mdx"]
spec["spec/*.yml, *.json"]
generated["features/docs/generated/**"]
refmdx["docs/ref/*.mdx"]
fed["External repos<br/>(federated)"]
end
subgraph render ["Rendering layer"]
features["features/docs/"]
components["components/"]
layouts["layouts/"]
end
guides --> mdx
guides --> trouble
guides --> fed
reference --> spec
reference --> generated
reference --> refmdx
guides --> features
reference --> features
features --> components
features --> layouts
```
## Top-level directory layout
| Path | Purpose | Notes |
| -------------------------------------- | ---------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| `app/` | Next.js App Router — thin route files that delegate to feature modules | Slug-based catch-alls per section, e.g. `guides/auth/[[...slug]]/page.tsx` |
| `apps/docs/content/guides/` | Source MDX for `/docs/guides/...` pages | One file per page; `_partials/` for shared blocks |
| `apps/docs/content/_partials/` | Reusable MDX snippets included via `<$Partial path="..." />` | Recursion supported |
| `apps/docs/content/troubleshooting/` | Troubleshooting articles | Some synced from GitHub issues via `Troubleshooting.script.mjs` |
| `apps/docs/components/` | React components used inside MDX | One folder per component or component family |
| `apps/docs/data/` | Typed data modules consumed by components | `.data.ts` suffix; lookup helpers live in `.utils.ts`, not here |
| `apps/docs/lib/` | Pure library code shared across pipelines | Schemas (zod), helpers (`.utils.ts`), tests |
| `apps/docs/features/docs/` | Page-level skeletons and the MDX renderer (`MdxBase`) | Shared `<Heading>` lives in `MdxBase.shared.tsx` |
| `apps/docs/features/` | Domain logic — docs rendering, auth, search/command menu, telemetry, app providers | `app.providers.tsx` wires React Query, theme, dev toolbar, command menu |
| `apps/docs/spec/` | Source-of-truth specs for reference generation | OpenAPI, SDK YAML, CLI config |
| `apps/docs/generator/` | Codegen templates for reference docs | |
| `apps/docs/resources/` | Data loaders (guide, reference, search, errors) | |
| `apps/docs/internals/` | Build-time markdown generation | **Do not import from runtime/client code** |
| `apps/docs/internals/markdown-schema/` | Per-component handlers: JSX → markdown string | File name matches the component name |
| `apps/docs/public/markdown/guides/` | Generated `.md` output; served via `/docs/guides/<path>.md` or `Accept: text/markdown` | Built by `generate-guides-markdown.ts`; see [`llm-agent-surface.md`](./llm-agent-surface.md) |
| `apps/docs/public/markdown/reference/` | Generated reference `.md` files | Built by `generate-reference-markdown.ts` |
| `apps/docs/middleware.ts` | Content negotiation for guides; bot rewrite for reference deep links | Uses `packages/common/markdown-negotiation.ts` |
| `apps/docs/app/api/guides-md/` | Serves pre-generated guide markdown to agents | Rewritten from `/docs/guides/<path>.md` |
| `apps/docs/examples/` | Copied from repo root `examples/` at build time | `codegen:examples` |
| `apps/docs/scripts/` | Build-time scripts (sitemap, markdown export, embeddings) | |
Published guide sections: `ai`, `api`, `auth`, `cron`, `database`, `deployment`,
`functions`, `getting-started`, `integrations`, `local-development`, `platform`,
`queues`, `realtime`, `resources`, `security`, `self-hosting`, `storage`,
`telemetry`. Each has its own `layout.tsx` for sidebar navigation.
## Content types
| Type | Location | Notes |
| ---------------------- | ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Guides / tutorials** | `content/guides/` | Hand-written MDX; goal-oriented |
| **Troubleshooting** | `content/troubleshooting/` | Partly synced from GitHub issues |
| **Reference** | Generated from `spec/` → `features/docs/generated/**` | Spec-driven (OpenAPI, SDKSpec, ConfigSpec, CLISpec). Reference pages do **not** use the standard MDX path — see [`docs-app-direction.md`](./docs-app-direction.md) for why. Management API OpenAPI path: [`management-api-reference.md`](./management-api-reference.md). |
| **Federated** | External repos at build time | Pulled via GitHub App. See [`federated-docs.md`](./federated-docs.md) |
## Routing model
Routes stay **thin**; rendering lives in **`features/docs/`**.
```tsx
// app/guides/getting-started/[[...slug]]/page.tsx
const slug = ['getting-started', ...(params.slug ?? [])]
const data = await getGuidesMarkdown(slug)
return <GuideTemplate {...data!} />
```
`getGuidesMarkdown()` (in `features/docs/GuidesMdx.utils.tsx`) loads the file,
validates frontmatter, checks navigation enablement, and returns data for
`GuideTemplate`.
`slug` is just the Next.js param name. `[[...slug]]` is an optional catch-all
(can match zero or more segments); `[...slug]` is required. The page handler
prepends the section name and joins segments to find content on disk.
For reference, `parseReferencePath(slug)` in `features/docs/Reference.utils.ts`
interprets segments like `javascript`, `v2`, `auth-signin` to pick SDK,
version, and section.
## The two pipelines (and why they share data)
The same content has to render twice:
1. **MDX runtime** — React components render `<MyComponent id="..." />`,
read data via a data registry, output HTML.
2. **Markdown export** — `internals/markdown-schema/<MyComponent>.ts`
handlers convert the same JSX into plain markdown for
`public/markdown/guides/`.
**Load-bearing rule:** both pipelines dereference the _same_ data
registry (typically `apps/docs/data/<topic>/index.ts` exporting an
ID-keyed map and a `getById` lookup). The component reads it; the
handler reads it. The JSX prop is just an `id`. This keeps the two
outputs in sync without a parallel data shape.
**Reference implementation: `ContentListings`** — the ID-keyed two-pipeline
pattern in production:
| Layer | Path |
| ----- | ---- |
| Data registry | `apps/docs/data/content-listings/` (`.data.ts` per topic + `index.ts`) |
| Runtime component | `apps/docs/components/ContentListings/` |
| Markdown handler | `apps/docs/internals/markdown-schema/ContentListings.ts` |
| MDX registration | `apps/docs/features/docs/MdxBase.shared.tsx` |
MDX usage: `<ContentListings id="storage-get-started" />`. For batch
overview-page migration, see the `audit-content-listings` skill in
[`docs-agent-skills`](https://github.com/supabase/docs-agent-skills).
When adding a new component that has a markdown representation:
1. Write the React component under `apps/docs/components/`.
2. Add a handler under `apps/docs/internals/markdown-schema/<SameName>.ts`.
3. Register the handler in `apps/docs/internals/generate-guides-markdown.ts`
(the `SCHEMA` object).
4. If a component is purely visual and should be **dropped** from markdown,
omit the handler — `generate-guides-markdown.ts` unwraps unknown JSX to its
children automatically.
For the full build flow (Turbo + pnpm lifecycle), see
[`build-pipeline.md`](./build-pipeline.md).
For how agents and LLMs consume exported markdown (negotiation, bulk
downloads, `llms.txt`), see [`llm-agent-surface.md`](./llm-agent-surface.md).
## The markdown export in 30 seconds
`generate-guides-markdown.ts` walks `content/guides/**/*.mdx`:
1. Parse MDX → mdast (mdx + gfm extensions).
2. Inline `<$Partial path="..." />` recursively.
3. `addBaseUrlPrefix(tree)` — prefixes internal links with `/docs/`.
4. `applySchema(tree, SCHEMA)` — bottom-up: serializes children first, then
replaces each JSX node with the result of its handler (or unwraps it).
5. Serialize mdast back to markdown.
6. Prepend front-matter-derived header (`# title`, subtitle, description).
7. Write to `public/markdown/guides/<same-path>.md`.
Each `SCHEMA` entry receives `{ props, children, node }` and returns the
markdown string to substitute.
## `<Heading>` and the prose typography contract
- `<Heading>` from `MdxBase.shared.tsx` is the canonical heading component
for MDX. It handles tag-from-level mapping and anchor IDs.
- The MDX wrapper applies prose styles to everything inside `.prose` (default
for guide pages). Inside `.not-prose` blocks, typography is opt-in.
- Pattern for new components with headings: render `<Heading>` _outside_
`not-prose`, render the structured layout _inside_ `not-prose`. The heading
inherits prose styles; the layout owns its own classes.
## Telemetry conventions
- Event names live in `packages/common/telemetry-constants.ts` (snake*case,
`docs*\*` prefix for docs events).
- Components call `useSendTelemetryEvent()` from `~/lib/telemetry`.
- Properties: prefer flat, ID-prefixed keys. Avoid optional spread tricks
unless a property is genuinely optional in the schema.
## Linting and verification entry points
See [`ci-and-lint.md`](./ci-and-lint.md) for the full CI surface. Local
commands:
| Tool | Where | What it catches |
| ------------------------------------------- | ----------- | --------------------------------------------------------- |
| `pnpm test:local <path>` (from `apps/docs`) | per-test | Vitest suite for `lib/` and `data/` schemas |
| `pnpm format` | repo root | Prettier — run before opening a PR |
| `pnpm lint --filter=docs` | repo root | ESLint over `apps/docs` |
| `pnpm typecheck` | repo root | TS across packages |
| `pnpm build --filter=docs` | repo root | Includes markdown generation; failures here block release |
| `pnpm run supa-mdx-lint` | `apps/docs` | MDX content lint |
Before adding a custom lint job, check whether the existing one can absorb
the check (see [`adding-features.md`](./adding-features.md) "Reuse
pipelines").
## Providers (`features/app.providers.tsx`)
Wraps the app with:
- `QueryClientProvider` (React Query)
- `FeatureFlagProvider`, `ThemeProvider` from `common`
- `TooltipProvider` from `ui`
- `DevToolbar` from `dev-tools`
- `DocsCommandProvider` / `DocsCommandMenu`
- `SiteLayout` from `layouts/`
## Troubleshooting page subtree
- `features/docs/Troubleshooting.page.tsx` — entry-level page renderer.
- `features/docs/Troubleshooting.utils.ts` — TS utils.
- `features/docs/Troubleshooting.utils.common.mjs` — `.mjs` because it's
consumed by both the Next.js build _and_ a Node sync script with import
resolution quirks. **Don't convert to `.ts`** without checking the sync
script.
- Topics enum in `TroubleshootingSchema` (the `topics` field) is the source
of truth for product tag values.
## Known integrations
- **Studio** (`apps/studio`) links to docs URLs — check link consistency when
changing URL shapes.
- **www** (`apps/www`) sometimes embeds docs sections.
- **PostHog** receives `docs_*` events for analytics.
- **`docs-agent-skills`** repo (https://github.com/supabase/docs-agent-skills)
holds batch audit/conversion skills that drive multi-PR docs migrations.
- **Federated upstream repos** — see [`federated-docs.md`](./federated-docs.md)
for the full list (pg_graphql, vecs, wrappers, terraform-provider, setup-cli,
splinter, agent-skills).
@@ -0,0 +1,167 @@
# Docs build pipeline
How `apps/docs` is built through Turborepo and pnpm lifecycle hooks.
Verify against the current `apps/docs/turbo.jsonc` and
`apps/docs/package.json` if behavior surprises you.
## Build flow
```mermaid
flowchart TB
subgraph turbo["Turborepo — apps/docs/turbo.jsonc"]
direction TB
WP["^build — workspace deps<br/>(common, ui, config, icons, …)"]
CE["codegen:examples<br/>copy ../../examples → apps/docs/examples"]
CR["codegen:references<br/>→ features/docs/generated/**"]
WP --> DOC["docs#build"]
CE --> DOC
CR --> DOC
end
subgraph npm["pnpm — apps/docs/package.json"]
direction TB
PRE["prebuild"]
NB["build — next build"]
POST["postbuild"]
PRE --> NB --> POST
end
DOC --> PRE
subgraph pre["prebuild steps"]
direction LR
GQL["codegen:graphql"]
REF["codegen:references"]
EX["codegen:examples"]
GM["build:markdown<br/>(guides + reference)"]
GZ["build:gz-archive"]
end
PRE --> GQL
PRE --> REF
PRE --> EX
PRE --> GM
PRE --> GZ
subgraph post["postbuild steps"]
direction LR
SM["build:sitemap"]
CDN["upload-static-assets.sh<br/>(R2, production-style deploys)"]
end
POST --> SM
POST --> CDN
```
## Commands
From the repo root:
- **Full graph:** `pnpm build` → `turbo run build` (builds every package/app
in dependency order).
- **Docs only:** `pnpm build:docs` → `turbo run build --filter=docs`.
```json
"build:docs": "turbo run build --filter=docs"
```
## What Turbo does
Root `turbo.jsonc` defines `build` with `dependsOn: ["^build"]`, so
**workspace dependencies** of `docs` (e.g. `common`, `ui`, `config`, …) are
built before `docs`.
`apps/docs/turbo.jsonc` **extends** that and tightens the docs task:
- **`build` also depends on** `codegen:examples` and `codegen:references`
(with explicit inputs/outputs so Turbo can cache them).
- It lists **env vars** that affect caching for the docs app.
- **`codegen:examples`** copies `../../examples` into `apps/docs/examples`;
**`codegen:references`** writes under `features/docs/generated/**`.
Pipeline Turbo orchestrates for docs: **dependency packages → example /
reference codegen → Next build**.
## What the `docs` package runs (pnpm lifecycle)
```json
"prebuild": "pnpm run codegen:graphql && pnpm run codegen:references && pnpm run codegen:examples && pnpm run build:markdown && pnpm run build:gz-archive",
"postbuild": "pnpm run build:sitemap && ./../../scripts/upload-static-assets.sh"
```
1. **`prebuild`** — GraphQL codegen, reference codegen, copy examples,
generate guides + reference markdown (`build:markdown`), then tarball
(`build:gz-archive`).
2. **`build`** — `next build`.
3. **`postbuild`** — sitemap, then `upload-static-assets.sh` (R2 CDN
upload on production-style deploys).
Same monorepo pattern as Studio / www (pnpm workspaces + Turbo `^build`),
with **extra codegen** wired as Turbo tasks and npm `pre`/`post` hooks around
`next build`.
## Codegen scripts
- **`codegen:graphql`** — generates GraphQL types from schema.
- **`codegen:examples`** — copies `examples/` from monorepo root into
`apps/docs/examples` for `$CodeSample` directive resolution.
- **`codegen:references:legacy`** — `features/docs/Reference.generated.script.ts`
(includes Management API: merge OpenAPI v1+v2 → `api.latest.*` JSON).
Spec download/bundle lives in `apps/docs/spec/Makefile` (Redocly);
see [`management-api-reference.md`](./management-api-reference.md).
- **`codegen:references:new`** — `scripts/build-reference-content.ts` (from
TSDoc JSON under `spec/reference/`). Output lands in
`features/docs/generated/**`.
- **`build:guides-markdown`** — runs `internals/generate-guides-markdown.ts`
over `content/guides/**/*.mdx`. Produces `public/markdown/guides/**.md`.
- **`build:reference-markdown`** — runs `internals/generate-reference-markdown.ts`.
Produces `public/markdown/reference/**.md`.
- **`build:markdown`** — shorthand for guides + reference markdown.
- **`build:gz-archive`** — runs `internals/generate-gz-archive.ts`.
Produces `public/docs.tar.gz` (tarball of all `public/markdown/`).
For how agents consume these outputs, see
[`llm-agent-surface.md`](./llm-agent-surface.md).
## Deploy vs CI
- **Production build** for the site is **Vercel**; `apps/docs/vercel.json`
sets `buildCommand` to `pnpm build` (the docs app's script chain above).
- **GitHub Actions** mostly run **tests** (`test:docs` → Turbo), **lint**,
**sync** jobs, and **smoke** tests — not a separate full production build
graph in the way Turbo does locally. See [`ci-and-lint.md`](./ci-and-lint.md).
## Local development
From `apps/docs`:
```bash
pnpm dev # http://localhost:3001/docs
```
- **`predev`** runs GraphQL and reference codegen plus example copy.
- A concurrent watcher syncs troubleshooting content
(`dev:watch:troubleshooting`).
- Community contributors: set `NEXT_PUBLIC_IS_PLATFORM=false` in `.env`.
- Supabase employees: `pnpm run dev:secrets:pull` for internal env vars
(AWS profile + `scripts/getSecrets.js`).
- In dev mode, the app **only builds routes upon request** rather than
pre-rendering — preview and production environments statically generate
routes during build for speed.
## Why this shape matters for changes
- Adding a new build step? See [`adding-features.md`](./adding-features.md)
"Reuse pipelines, don't fork them" and check if `prebuild` or `postbuild`
already has a hook that fits.
- New env var? Add it to `apps/docs/turbo.jsonc`'s `env` so Turbo doesn't
cache stale.
- Touching the markdown export? Read [`app-map.md`](./app-map.md) "The two
pipelines" first — runtime and export share data, not code paths.
## Related
- [`app-map.md`](./app-map.md) — directory layout and runtime / export split.
- [`ci-and-lint.md`](./ci-and-lint.md) — GitHub Actions workflow surface.
- [`federated-docs.md`](./federated-docs.md) — external content fetched
during build.
@@ -0,0 +1,131 @@
# Docs CI and lint surface
GitHub Actions running on every PR touching `supabase/supabase`, plus
the deploy path. Confirm against the current `.github/workflows/` if
behavior surprises you.
Before adding a new lint job or CI check, **scan this list first** — see
[`adding-features.md`](./adding-features.md) "Reuse pipelines, don't fork them."
## PR flow
```
PR opened / updated
│
├── docs_lint (MDX/content linting; required)
├── docs_lint_comment_external (posts results as PR comments for external PRs)
├── Docs Tests (pnpm test:docs on .ts* file changes)
├── TypeScript & Lint (tsc + eslint)
├── Prettier (format check)
├── reviewdog (inline annotations)
├── Validate pull request (PR metadata)
└── Authorize Vercel Deploys → Vercel builds preview deploy → Preview on CDN
Merge to master
└── Vercel → next build (with prebuild markdown generation) → deploy
```
## On every pull request
Workflows run in parallel against any PR touching the repo:
### 1. `docs_lint`
Primary docs-specific CI check. MDX/content linting for the docs. **Required
check before merging.** Backed by `supa-mdx-lint`.
### 2. Docs Tests (`docs-tests.yml`)
Triggered on PRs to master when files matching `apps/docs/**/*.ts*` or
`apps/docs/spec/**/*.json` change. Runs on a Blacksmith 4-vCPU Ubuntu runner
with concurrency controls to cancel stale builds.
- Sparse checkout of `apps/docs`, `examples`, `packages`, `supabase`, and
`patches`.
- Install pnpm (pinned hash).
- Set up Node.js from `.nvmrc`.
- `pnpm install --frozen-lockfile`.
- Run `pnpm run test:docs` (with dummy GitHub OAuth env vars to prevent local
Supabase startup errors).
### 3. TypeScript & Lint (`typecheck.yml`)
TypeScript type checking and ESLint across the monorepo.
### 4. Prettier (`prettier.yml`)
Format checking across the whole repo including `apps/docs`.
### 5. `docs_lint_comment_external`
Companion to `docs_lint` that posts lint results as PR comments for external
contributors.
### 6. Authorize Vercel Deploys
Gates Vercel preview deployment on a GitHub-side check first. Prevents
arbitrary forks from triggering Vercel builds.
### 7. reviewdog
Inline code review annotations via reviewdog.
### 8. Validate pull request
PR metadata validation (title format, labels, etc.).
## Deployment (on merge to master)
Production deployment is handled by **Vercel**. Supabase employees branch the
repo directly rather than fork it, so CI checks auto-run and Vercel deploys
can be authorized without the external PR security gate.
Key deployment detail: the project generates markdown files for each page
under `/docs/guides/..` as a **prebuild task**. This lets Vercel bundle these
files with middleware and functions at build time.
The **Authorize Vercel Deploys** workflow is the glue between GitHub Actions
and Vercel: it runs first to approve the deploy, then Vercel picks it up and
builds/deploys the Next.js site to their CDN.
## Notable design choices
- **Sparse checkout** keeps CI fast by only pulling the relevant workspace
packages.
- **Blacksmith runners** are used instead of standard GitHub-hosted runners
for speed.
- All workflow steps use **pinned action hashes** rather than floating tags
for supply chain security.
## Where to add a new check
Before adding a new GitHub Actions workflow:
1. **Can `docs_lint` absorb it?** — most MDX/content checks belong inside
`supa-mdx-lint` configuration, not as a new workflow.
2. **Can `Docs Tests` absorb it?** — TypeScript / vitest checks for new
functionality fit here.
3. **Is it cross-cutting?** — typecheck, prettier, and reviewdog already
cover the cross-cutting cases.
4. **Last resort** — a new workflow file. Use a Blacksmith runner, sparse
checkout, pinned action hashes, and a concurrency group. Add a clear
trigger filter so it doesn't run on unrelated PRs.
## Vale and prose linting (future)
Chris Ward's wishlist (source: hiring conversations, not yet in the repo):
- Vale linting MCP server.
- Vale extension for VS Code.
- More async automation that doesn't block PR merges.
If adding prose linting, prefer async (commenting) checks over blocking ones
to keep merge cadence high.
## Related
- [`app-map.md`](./app-map.md) — what `pnpm test:docs` actually runs.
- [`build-pipeline.md`](./build-pipeline.md) — `prebuild` step that ships
markdown to Vercel.
- [`adding-features.md`](./adding-features.md) — reuse existing pipelines
before adding new ones.
@@ -0,0 +1,63 @@
# `apps/docs` direction and vision
Where the docs app is heading and what new work should align with. For
the _current_ state of broken or fragile systems, see
[`known-issues.md`](./known-issues.md). For feature-design best
practices, see [`adding-features.md`](./adding-features.md).
## Refactoring vision
- **Docs-only project.** The long-term goal is for the documentation
project to handle _only_ documentation. Other functionalities (specific
APIs, etc.) should migrate to sub-projects.
- **Ongoing surface reduction.** A substantial portion of legacy
critical code has already been refactored, and remaining work
continues to reduce surface area rather than add to it.
- **One-to-one markdown fidelity.** Improvements to
`generate-guides-markdown` aim for exact correspondence between the
rendered guide and the LLM-oriented markdown export. New components
that render in MDX should serialize to markdown with the same
semantics.
## Working norms
- **Work inside `apps/docs/`, not the repo root.** Simpler terminal
commands, clearer debugging scope. Most docs commands assume you've
`cd`'d into the app.
- **AI use is encouraged** for explaining complex or legacy sections of
the codebase. Considered effective for mechanical, non-creative tasks.
- **Historical context.** For decisions older than the current
refactoring wave, check with longer-tenured maintainers — most
current code has clearer provenance.
## What this means for new work
When proposing changes that touch `apps/docs`:
1. **Reuse existing seams before adding new ones.** Markdown export, MDX
pipeline, lint, codegen, and federated fetch are all places where
new surface area gets challenged. See
[`adding-features.md`](./adding-features.md) "Inventory before you
write."
2. **Keep markdown fidelity in mind.** Anything that changes how a
guide renders should also change how it serializes to markdown — or
have a clear reason it doesn't.
3. **Don't build against the broken parts.** See
[`known-issues.md`](./known-issues.md). Search, Sentry
instrumentation, and federated link handling are all in flux. New
features should not depend on their current shape.
4. **Don't extend tech debt.** Component scatter, parallel pipelines,
and bespoke render paths are existing problems — don't add to them.
5. **Refactor only what the feature requires.** Bundling unrelated
cleanup into a feature PR muddies review and gets cut anyway. Open a
separate refactor PR if the cleanup matters.
## Related
- [`adding-features.md`](./adding-features.md) — feature-design best
practices.
- [`known-issues.md`](./known-issues.md) — what's currently broken or
fragile.
- [`app-map.md`](./app-map.md) — where the existing seams are now.
- [`federated-docs.md`](./federated-docs.md) — mechanics of one of the
known liabilities.
@@ -0,0 +1,227 @@
# Federated docs
`apps/docs` can pull **markdown (and other files) from external GitHub repos**
at **build or request time**, then render them inside the normal docs shell.
Content lives in ecosystem repos (client libs, extensions, tooling) without
duplicating into `apps/docs/content`.
Documented upstream in root **`DEVELOPERS.md`** → "Federated docs."
> **The maintainers treat this as a known liability.** See
> [`docs-app-direction.md`](./docs-app-direction.md) for the full context
> (lack of editorial oversight, brittle builds, quality drift). Treat
> federated content as a liability when reviewing or debugging.
## Why federate
- **No duplication** — e.g. `supabase/vecs` docs stay in the vecs repo.
- **Automatic sync** — fetched during the docs build (or on cached
revalidation), not hand-copied.
- **Native feel** — same `GuideTemplate`, nav, typography, and search surface
as first-party guides.
- **Flexible placement** — can embed external docs at nearly any path under
`/guides/`.
Trade-off: **integration is manual and brittle**. Each federated section
needs its own route file, `pageMap`, link transform, and often custom remark
plugins.
## Build-time flow
```mermaid
flowchart TB
subgraph upstream ["External repos"]
PG["supabase/pg_graphql"]
VECS["supabase/vecs"]
WRAP["supabase/wrappers"]
TF["supabase/terraform-provider-supabase"]
CI["supabase/setup-cli"]
SPL["supabase/splinter"]
SKILLS["supabase/agent-skills"]
end
subgraph fetch ["Fetch layer — lib/octokit.ts"]
APP["GitHub App auth<br/>DOCS_GITHUB_APP_*"]
API["getGitHubFileContents()"]
CACHE["fetchRevalidatePerDay<br/>(Next.js cache, retry up to 5x)"]
APP --> API --> CACHE
end
subgraph route ["Per-section route — app/guides/.../page.tsx"]
MAP["pageMap<br/>(slug → remoteFile + meta)"]
GET["getContent()"]
MAP --> GET
end
subgraph transform ["MDX pipeline"]
RM["remark plugins<br/>Admonition, Tabs, removeTitle"]
RH["rehype plugins<br/>linkTransform + rehypeSlug"]
RM --> RH
end
subgraph render ["Render"]
GT["GuideTemplate / GuideMdxContent"]
OUT["/docs/guides/..."]
GT --> OUT
end
upstream --> API
GET --> RM
RH --> GT
```
### Step-by-step
1. **Route file** defines `org`, `repo`, `branch` (or tag), `docsDir`, and a
**`pageMap`** — each entry maps a local slug to a remote markdown file
plus page metadata.
2. **`getGitHubFileContents()`** fetches file content via a **GitHub App**
(`DOCS_GITHUB_APP_ID`, `DOCS_GITHUB_APP_INSTALLATION_ID`,
`DOCS_GITHUB_APP_PRIVATE_KEY`). Uses **once-per-day revalidation** by
default (`fetchRevalidatePerDay`).
3. Raw markdown is passed through **remark/rehype plugins** to bridge dialect
gaps.
4. **`linkTransform` + custom `urlTransform`** rewrite relative links from
the source repo into `/docs/guides/...` paths (or fall back to the upstream
docs site / GitHub).
5. Output is rendered with **`GuideTemplate`** so federated pages look like
native guides. **Edit on GitHub** links point at the source repo file.
> Note: `DEVELOPERS.md` still mentions `getStaticProps()` — the current App
> Router implementation uses async server components + `generateStaticParams`
> instead, but the idea is the same: fetch remote markdown at build time.
## Federated sources (inventory)
| Local path | Source repo | Ref / branch | Pattern |
| ---------------------------------------- | -------------------------------------- | ------------------------------ | ------------------------------------------------- |
| `/guides/graphql/*` | `supabase/pg_graphql` | `master` | Fully federated; `pageMap` per page |
| `/guides/ai/python/*` | `supabase/vecs` | `main` | Fully federated |
| `/guides/deployment/ci/*` | `supabase/setup-cli` | `gh-pages` | Fully federated |
| `/guides/deployment/terraform/*` | `supabase/terraform-provider-supabase` | branch in `terraformConstants` | Federated prose pages |
| `/guides/deployment/terraform/reference` | same | same | Federated **JSON schema** (not MDX) |
| `/guides/database/extensions/wrappers/*` | `supabase/wrappers` | **release tag** `docs_v*.*.*` | **Hybrid** — local MDX + federated catalog |
| `/guides/database/database-advisors` | `supabase/splinter` | `main` | **Dynamic listing** — all `docs/*.md` files |
| AI Skills index | `supabase/agent-skills` | `main` | Lists `skills/*/SKILL.md` at runtime |
| `$CodeSample` directive | various | commit SHA only | Snippets via `getGitHubFileContentsImmutableOnly` |
### Related patterns (not quite "federated docs")
| Pattern | Source | Mechanism |
| -------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------ |
| **AI prompts** | `examples/prompts/*.md` | Copied into `apps/docs/examples` at build (`codegen:examples`); nav injected in getting-started layout |
| **Integrations nav** | Supabase `partners` table | `integrations/layout.tsx` fetches approved partners (external URLs) |
| **Reference docs** | `apps/docs/spec/` | Generated locally, not fetched from GitHub — see [`build-pipeline.md`](./build-pipeline.md) |
## How to spot a federated page in the repo
- Look for a **dynamic route** (`[[...slug]]/page.tsx`, `[slug]/page.tsx`)
instead of content-only routing.
- Search for **`// We fetch these docs at build time from an external repo`**
or **`getGitHubFileContents`**.
- Check for a local **`pageMap`** array mapping slugs to `remoteFile` names.
- GraphQL is listed in `PUBLISHED_SECTIONS` comments as _"technically
published, but completely federated"_ — no MDX under `content/guides/graphql/`.
Canonical starter example:
`apps/docs/app/guides/ai/python/[slug]/page.tsx` (vecs).
## Link and markdown transforms
External repos often use **MkDocs Material** or similar dialects. Supabase
docs do not understand those natively.
### Remark plugins (`lib/mdx/plugins/`)
| Plugin | Purpose |
| ------------------- | ----------------------------------------------- |
| `remarkAdmonition` | `!!! note` / `!!! warning` → `<Admonition>` |
| `remarkTabs` | pymdownx tab syntax → Supabase tabs |
| `remarkRemoveTitle` | Strip duplicate H1 when title comes from `meta` |
New upstream syntax may need a **new remark plugin** — called out explicitly
in `DEVELOPERS.md`.
### Link transform (`rehypeLinkTransform.ts`)
Each federated route defines its own **`urlTransform`** function:
- **Relative `.md` links** → mapped via `pageMap` to `/docs/guides/...` (or
section-relative slug).
- **Unmapped relative links** → fall back to upstream site (e.g.
`https://supabase.github.io/vecs/...`) or raw GitHub blob URL (terraform).
- **Absolute URLs** → passed through unchanged.
Wrappers additionally rewrites **`../assets/`** image paths to
`raw.githubusercontent.com` URLs tied to the docs release tag.
## Known failure modes
Federated docs are a frequent source of **content quality issues** and
**broken links**.
### Broken or wrong links
- **`pageMap` is manual** — upstream rename/remove → broken link until
someone updates the route file in `supabase/supabase`.
- **`urlTransform` is per-section** — logic differs between graphql, vecs,
terraform, wrappers, etc.
- **Unmapped pages fall back externally** — users leave the docs site or land
on a GitHub pages URL that may not match the embedded path structure.
- **Absolute links in upstream** pass through untouched — may point at old
domains or anchors that moved.
- **Cross-links between federated and native guides** are not automatic;
upstream authors don't know Supabase URL shapes.
### Content quality
- **No editorial gate in `apps/docs/content`** — upstream tone, structure,
and freshness vary.
- **Dialect mismatches** — unsupported mkdocs extensions render as raw text
or fail MDX compile until a plugin is added.
- **Duplicate titles** — mitigated by `removeTitle` / `removeRedundantH1`,
but layout can still look off.
- **Stale content** — daily GitHub cache means upstream fixes may not appear
until revalidation; wrappers pin to **release tags** which can lag further.
### Operational / dev failures
- **GitHub App credentials required** — without `DOCS_GITHUB_APP_*`, fetches
fail. `$CodeSample` with external repos shows a **local dev warning** and
does not render real content.
- **GitHub API timeouts** — `database-advisors` catches fetch errors and
shows a degraded view with a link to GitHub; build may still succeed with
partial content.
- **Wrappers tag resolution** — `getLatestDocsTag()` queries `docs_v*.*.*`
tags; if tagging breaks, federated wrapper pages throw at build time.
- **Dev mode gaps** — some federated routes return **empty
`generateStaticParams` in dev** (`IS_DEV`), so pages may 404 locally unless
you hit them via production build or adjust params.
### Navigation drift
- Sidebar entries for federated sections are often **hard-coded** in
`NavigationMenu.constants.ts` plus optional **`additionalNavItems`**
injection (prompts, integrations).
- New upstream pages do not appear in nav until a maintainer adds them to
`pageMap` **and** navigation config.
## When to federate vs copy into `content/`
Federate when:
- The canonical docs live in another repo and change frequently.
- Maintainers of that repo own the content lifecycle.
Prefer `content/guides/` when:
- Editorial control, consistent voice, and link stability matter.
- Content is tightly coupled to Supabase product UI and cross-links.
## Related
- [`app-map.md`](./app-map.md) — runtime + markdown export pipelines.
- [`build-pipeline.md`](./build-pipeline.md) — where federated fetches sit
in the overall build.
- [`docs-app-direction.md`](./docs-app-direction.md) — context on the
federated-docs liability and long-term direction.
@@ -0,0 +1,139 @@
# `apps/docs` gotchas
Specific traps to watch for. One-liner per item.
## Markdown pipeline
- **JSX with no schema handler is unwrapped, not dropped.** Children pass
through as plain markdown. If a component should disappear entirely from
`.md` output, its handler must return `''`.
- **Handlers run bottom-up.** Children are already-serialized markdown
strings by the time the parent handler sees them. Don't try to inspect
child JSX from a parent handler — use `node.children` only for structure
decisions.
- **`mdxFlowExpression` / `mdxTextExpression` / `mdxjsEsm` are skipped.**
Anything wrapped in `{...}` won't appear in markdown output.
- **`AiPrompt` prompts must be a `prompt={...}` prop, not children.** The
schema handler decodes the raw JS string literal from `propsFrom()`
(Prettier may emit single-quoted multiline forms). See
`internals/markdown-schema/AiPrompt.ts`.
- **`<$Partial>` recursion is silent.** A missing or unreadable partial is
dropped without error. Check `partials/` paths when content seems missing
from generated `.md`.
- **`$Partial` `variables` are runtime-only unless wired in markdown export.**
`partialsRemark` substitutes `{{ .key }}` placeholders; `inlinePartials`
in `generate-guides-markdown.ts` must do the same or nested partials like
`path="{{ .prompt }}"` fail silently (file not found).
- **`GlassPanel` / `IconPanel` markdown handlers must include children.**
A handler that returns only `props.title` drops nested content. See
`internals/markdown-schema/Panel.ts`.
## Markdown handler authoring
- Handler files in `internals/markdown-schema/` must be named after the
component (`ContentListings.ts`, not `Listings.ts`). The registry key in
`generate-guides-markdown.ts` is the JSX element name.
- Use the existing `withDocsBasePath` / `addBaseUrlPrefix` from
`internal-links.ts` for URL prefixing — don't roll your own.
- If a handler needs data, the **JSX prop carries an `id`** and the handler
looks the data up from the same registry the runtime component uses. Don't
serialize data into JSX props.
- Push a blank line right after each exported block (heading, description,
list). Keeps handler logic linear and avoids redundant trailing-empty-line
branches.
## React / MDX components
- `not-prose` is the canonical opt-out from the surrounding `.prose`
typography. Wrap structural layout in `not-prose`; keep headings outside so
they inherit prose styles.
- `<Heading>` from `MdxBase.shared.tsx` is the canonical heading. Don't roll
your own `<h2>` / `<h3>` markup in MDX components — typography drifts.
- `<Link>` from `next/link` works with `target="_blank"` for external
destinations. Don't construct `<a>` directly.
- A `useSendTelemetryEvent()` call inside a render path returns a function;
don't invoke it directly inside the JSX, build a callback first.
## Data modules
- Shared zod schemas for MDX components should use HTML heading levels
(`'h2'|'h3'|'h4'`), not markdown marker strings (`'##'`). Markdown markers
belong only in `internals/markdown-schema/` handlers (via a `Record`/`Map`
lookup).
- `getXById(id)` lookups belong in `*.utils.ts`, not in the data file or the
schema file. Schema files hold schemas; data files hold data; utils hold
functions.
- Each `.data.ts` exports named groups; the topic `index.ts` aggregates them
into a `Record<id, group>` map. The aggregation file is the single source
of truth for "all valid IDs."
## URL handling
- External-link detection is **not** just `/^https?:\/\//`. Protocol-relative
(`//host`), `mailto:`, `tel:`, custom schemes — all external. Prefer
`new URL(href, base)` or a broader regex.
- `withDocsBasePath('/guides/foo')` → `/docs/guides/foo`. Already-prefixed
URLs are passed through. Use it consistently in markdown output to avoid
broken links.
## Troubleshooting subtree
- `Troubleshooting.utils.common.mjs` is `.mjs` because the troubleshooting
sync script can't resolve `.ts` imports cleanly. Don't convert it.
- Rebasing can resurrect a **stale** version of this file when you reset
unrelated changes — verify against current `master` after
`git checkout master -- <path>`.
- The `topics` enum in `TroubleshootingSchema` is hand-maintained. Adding a
new product means updating that enum.
## Build / CI
- `pnpm build --filter=docs` runs markdown generation. A failure in
`generate-guides-markdown.ts` blocks the build — read its console output
rather than the cryptic CI error.
- `pnpm format` from the repo root catches most prettier drift. Run it
before opening a PR; CI is unforgiving.
- Auto-import sorting can silently shuffle imports across files you didn't
touch. Reset those files (`git checkout master -- <path>`) before pushing.
## Federated docs
See [`federated-docs.md`](./federated-docs.md) for the full surface — the
short version:
- **No GitHub App credentials in dev** = `$CodeSample` shows a warning, not
real content. Don't assume the page is broken.
- **`pageMap` is manual.** Upstream renames break links until a maintainer
updates the route file.
- **Wrappers pin to release tags** (`docs_v*.*.*`). If tagging breaks
upstream, build fails.
- **Search and federated content interact poorly** — search infrastructure
is currently decoupled from the markdown generation. See
[`docs-app-direction.md`](./docs-app-direction.md).
## Reference pages
- SDK and CLI reference pages **don't go through the standard MDX path** —
they're too long for the AST parser in preview environments. Generated
from `spec/` instead, output to `features/docs/generated/**`.
- Reference page length is a known UX/LLM problem. Long-term plan: split
into smaller modular pages. Management API is already one endpoint per
page (DOCS-1268); see [`management-api-reference.md`](./management-api-reference.md).
- Management API Redocly bundle **omits `--dereferenced`** because of a
circular `$ref`. Don't "fix" that by adding `--dereferenced` without
checking codegen's cycle-safe `resolveRefs`.
- Don't propose Scalar/Redoc/Elements as the Management API page UI
without covering markdown export, search, and `x-*` extension fields.
## PR hygiene
- No incidental import reorders or whitespace edits in a feature PR. Reset
files that are conceptually unchanged.
- When resetting unrelated files (`git checkout master -- <path>`), verify the
result matches current `master` — rebases can resurrect stale file versions.
- File names must match the primary export. Rename rather than aliasing on
import.
- Single-use helpers stay in the consuming file. Don't add new files in
unrelated folders for one-shot utilities.
- Two components with the same JSX shape and different classes → collapse
with a discriminator prop. Don't ship near-duplicates.
@@ -0,0 +1,140 @@
# `apps/docs` known issues
What's currently broken, fragile, or intentionally being avoided. Read
this before depending on a piece of infrastructure for new work — if it's
listed here, expect it to change or fail in surprising ways.
Living document — update when a new fragility shows up in a PR or
incident.
## Federated documentation pipeline
**Severity:** load-bearing fragility. Affects daily builds.
The docs site fetches markdown from external repos at build/request time
(pg_graphql, vecs, wrappers, terraform-provider, setup-cli, splinter,
agent-skills). Treat as a liability:
- **Lack of oversight.** Quality control, linting, and formatting can't
be enforced because the source code isn't owned.
- **Quality drift.** Inconsistent formatting, missing semicolons,
structural differences between federated and in-house content.
- **Build brittleness.** Upstream fetches fail intermittently — retry
logic (up to 5x) is in place to mitigate. Builds can still degrade or
partially succeed.
- **Manual `pageMap`.** Upstream rename/remove → broken links until a
maintainer updates the route file in `supabase/supabase`.
- **Wrappers pins to release tags** (`docs_v*.*.*`). If tagging breaks
upstream, federated wrapper pages throw at build time.
- **Dev mode gaps.** Some federated routes return empty
`generateStaticParams` in dev — pages may 404 locally unless hit via
production build.
See [`federated-docs.md`](./federated-docs.md) for the full mechanics and
failure modes. Implication: **don't add new federated sources** without
strong justification; **don't extend federated patterns** to new content
types.
## Troubleshooting guides currently live in an external repo
**Severity:** ongoing sync issues.
This causes synchronization problems between the docs site and the source
of truth. Proposed direction: bring them in-house and require support
teams to contribute directly to `supabase/supabase` to ensure accuracy.
## Reference pages can't go through standard MDX
**Severity:** architectural constraint.
SDK & CLI reference pages (e.g. JavaScript SDK, CLI) use a different
rendering path than guides. Attempts to migrate them to MDX **failed
because their length exceeded memory limits for the AST parser in
preview environments.** They are generated from `spec/` instead, output
to `features/docs/generated/**`.
Implication: don't propose "unify everything under MDX" for reference
pages without addressing the memory ceiling first.
## Reference page length
**Severity:** UX and LLM usability issue.
Current reference pages are far too long, making them difficult for
human users to navigate and inefficient for LLMs to process. Long-term
goal: break them into specialized, modular pages for better readability
and performance.
Implication: new reference content should default to smaller, modular
pages.
## Search infrastructure is decoupled and considered broken
**Severity:** functional, but not trustworthy for new features.
Search relies on separate scripts and embeddings rather than the
consolidated markdown files (`build:guides-markdown` output). It is
decoupled from the main markdown-generation pipeline.
Implication: **don't build new features against the current search
infrastructure** assuming it's stable. Expect it to be reworked as part
of the refactoring direction.
## Error tracking and Sentry signal quality
**Severity:** noise-to-signal issue.
Sentry errors for slugs / 404s are often caused by:
- Bots scanning paths.
- Legacy URLs that no longer exist.
- Broken inbound links from external sites.
Error tracking and instrumentation are relatively new and
underdeveloped. Implication: **don't trust noisy Sentry signals as proof
of a real bug** without corroboration (reproduce locally, check the
referrer, etc.).
## Component dispersion
**Severity:** tech debt — accumulating cost, not a runtime failure.
React components for the docs app are inconsistently scattered across
multiple folders (`components/`, `features/docs/`, `features/ui/`,
`layouts/`, etc.). No canonical place for "this is a docs component."
Implication:
- Don't try to refactor the scatter as a side-quest in a feature PR.
- Place new components by following the closest existing analogue, not
by inventing a new location.
- Prioritize understanding the immediate task over mastering the whole
codebase.
## "One-to-one" markdown fidelity is aspirational, not enforced
**Severity:** ongoing improvement target.
The stated direction is one-to-one fidelity between rendered guides and
the markdown export produced by `generate-guides-markdown`. In practice,
gaps exist:
- Any MDX component without a `markdown-schema` handler is unwrapped to
its children, which may not match the rendered output.
- `$Partial` recursion is silent — missing partials are dropped.
- Some components (interactive, JSX-expression-heavy) inherently can't
serialize to markdown faithfully.
Implication: when adding a component that should appear in markdown
output, **always add a handler** in `internals/markdown-schema/` and
register it in `generate-guides-markdown.ts`. See
[`app-map.md`](./app-map.md) "The two pipelines."
## Related
- [`docs-app-direction.md`](./docs-app-direction.md) — where the
refactoring is headed; what these issues are being driven toward.
- [`federated-docs.md`](./federated-docs.md) — full mechanics of the
federation pipeline.
- [`gotchas.md`](./gotchas.md) — smaller per-change traps; this file
is for load-bearing systemic issues.
@@ -0,0 +1,97 @@
# LLM / agent content parity and onboarding
Companion to [`llm-agent-surface.md`](./llm-agent-surface.md) (routing and
discovery). This file covers HTML↔markdown fidelity, search caveats, agent
onboarding guides, and known stale wiring.
**Verify against live code** before depending on any path here.
## Two-pipeline parity for embedded content
Content that renders on the HTML page is **not** automatically present
in the `.md` export. Both pipelines must implement the same semantics.
Example: framework quickstart **AI prompt blocks** (see
supabase/supabase#47543). Each quickstart includes a bare partial:
```mdx
<$Partial path="ai/quickstart_prompt_nextjs.mdx" />
```
The partial is a self-closing `<AiPrompt>` with the prompt as a **prop**
(not children — expression children are skipped by the guides markdown
pipeline):
```mdx
<AiPrompt
prompt={
'Help me add Supabase to my Next.js project. Create a Supabase project at\ndatabase.new and run the instruments table SQL. Then:\n1. …'
}
/>
```
| Pipeline | Path |
| ------------ | ------------------------------------------------------------ |
| **HTML** | `features/ui/AiPrompt.tsx` → `PromptPanel` (copy + expand) |
| **Markdown** | `internals/markdown-schema/AiPrompt.ts` reads `props.prompt` |
`propsFrom()` stores the raw JS expression source. Prettier formats the
prop as a multiline **single-quoted** string; the schema handler must
decode that literal (trim + escapes), not only `JSON.parse` double-quoted
JSON. Symptom if decoding is wrong: generated `.md` keeps surrounding
quotes and literal `\n`.
`$Partial` variable substitution (`lib/partials.utils.ts`, wired in both
`partialsRemark` and `inlinePartials`) remains required for other nested
partials — it is no longer the AI-prompt path.
When adding content aimed at both humans and agents, always verify:
```bash
cd apps/docs && pnpm build:guides-markdown
# then inspect public/markdown/guides/<same-path>.md for **AI Prompt**
```
## Programmatic search
`/docs/api/graphql` exposes `searchDocs`, backed by embeddings generated
offline via `scripts/search/generate-embeddings.ts`. Agents can request
full `content` in results.
**Caveat:** search infrastructure is decoupled from the markdown export
pipeline and is considered fragile. See
[`known-issues.md`](./known-issues.md) "Search infrastructure is
decoupled and considered broken" before building features on top of it.
## Agent onboarding content
First-party guides under `content/guides/ai-tools/`:
| Page | Purpose |
| ---------------- | --------------------------------- |
| `mcp.mdx` | Supabase MCP server setup |
| `plugins.mdx` | One-click MCP + skills bundle |
| `byo-mcp.mdx` | Build your own MCP server |
| `ai-skills.mdx` | Index of installable agent skills |
| `ai-prompts.mdx` | Curated IDE prompts |
The skills index is **federated** from `supabase/agent-skills` at
runtime (`AiSkills.utils.ts`). See [`federated-docs.md`](./federated-docs.md).
## In-flux / stale wiring
- `apps/docs/.gitignore` lists `public/llms/` as generated by
`build:llms`, but **`build:llms` is not in `package.json`** today.
Per-source `llms/*.txt` links in `/llms.txt` may point at assets whose
generation path is unclear — verify before depending on them.
- `llms-full.txt` fetches reference markdown from
`${NEXT_PUBLIC_DOCS_URL}/markdown/reference/<slug>.md` at runtime.
## Related
- [`llm-agent-surface.md`](./llm-agent-surface.md) — routing, negotiation,
entry points, bulk vs per-page retrieval.
- [`app-map.md`](./app-map.md) — two-pipeline model, directory layout.
- [`federated-docs.md`](./federated-docs.md) — agent-skills federation.
- [`known-issues.md`](./known-issues.md) — search fragility, reference
page length.
@@ -0,0 +1,181 @@
# LLM and agent consumption surface
How `apps/docs` exposes machine-readable content and how that relates to
`apps/www`. For markdown export fidelity and agent onboarding pages, see
[`llm-agent-parity.md`](./llm-agent-parity.md). For the markdown export
pipeline itself, see [`app-map.md`](./app-map.md) "The two pipelines."
**Verify against live code** — several pieces here span two apps and
change independently.
## Scope boundary
| App | Role |
| --------------- | ------------------------------------------------------------------------------------------------------------------ |
| **`apps/docs`** | Produces markdown exports, serves per-guide `.md`, reference static files, GraphQL search, agent onboarding guides |
| **`apps/www`** | Serves discovery indexes (`/llms.txt`, `/llms-full.txt`) and product-page `.md` negotiation |
`llms.txt` is **not** a route in `apps/docs`. It is assembled at runtime
by `apps/www/app/llms.txt/route.ts`, reading guide directory names from
`apps/docs/content/guides/` and linking to docs + reference exports.
There is no static `llms.txt` file in the repo — the www route handler
builds it on each request.
### Key source files
| What | Monorepo path | Public URL (prod) |
| --------------------- | ------------------------------------------------ | ------------------------------------------------- |
| `llms.txt` route | `apps/www/app/llms.txt/route.ts` | `https://supabase.com/llms.txt` |
| `llms-full.txt` route | `apps/www/app/llms-full.txt/route.ts` | `https://supabase.com/llms-full.txt` |
| Guide middleware | `apps/docs/middleware.ts` | — (lines ~16–38: negotiate, rewrite to guides-md) |
| WWW middleware | `apps/www/middleware.ts` | Product-page markdown negotiation |
| Negotiation logic | `packages/common/markdown-negotiation.ts` | Shared by both middleware files |
| Guides markdown API | `apps/docs/app/api/guides-md/[...slug]/route.ts` | Reads `public/markdown/guides/` at runtime |
| Markdown manifest | `apps/docs/public/markdown/manifest.json` | Slugs eligible for `.md` negotiation |
## Build outputs → serving paths
```mermaid
flowchart TB
subgraph docsApp ["apps/docs prebuild"]
GM["build:guides-markdown"]
RM["build:reference-markdown"]
GZ["build:gz-archive"]
GM --> outGuides["public/markdown/guides/"]
RM --> outRef["public/markdown/reference/"]
GZ --> tarball["public/docs.tar.gz"]
end
subgraph docsServe ["apps/docs runtime"]
mw["middleware.ts"]
guidesMd["api/guides-md"]
crawlers["api/crawlers"]
gql["api/graphql searchDocs"]
mw --> guidesMd
end
subgraph wwwApp ["apps/www"]
llmsTxt["/llms.txt"]
llmsFull["/llms-full.txt"]
end
outGuides --> guidesMd
outGuides --> llmsFull
outRef --> llmsFull
outGuides --> llmsTxt
gql --> guidesMd
```
## Entry points
| URL | Owner | Source |
| ---------------------------------------- | ----------- | --------------------------------------------------------------------------------------------- |
| `/llms.txt` | `apps/www` | `app/llms.txt/route.ts` — index of guide sections + reference links |
| `/llms-full.txt` | `apps/www` | `app/llms-full.txt/route.ts` — concatenated guides + reference + product overviews |
| `/docs/guides/<path>.md` | `apps/docs` | Middleware → `app/api/guides-md/[...slug]/route.ts` reads `public/markdown/guides/` |
| `/docs/markdown/reference/<lib>.md` | `apps/docs` | Static file from `public/markdown/reference/` (build output) |
| `/docs/docs.tar.gz` | `apps/docs` | `internals/generate-gz-archive.ts` tarballs all of `public/markdown/` |
| `/docs/api/graphql` (`searchDocs`) | `apps/docs` | Vector search over embedded doc sections — see [`llm-agent-parity.md`](./llm-agent-parity.md) |
| `/docs/reference/<sdk>/<section>` (bots) | `apps/docs` | `isbot()` → `app/api/crawlers/route.ts` (simplified HTML, not markdown) |
Homepage alternate link points agents at the full dump:
`https://supabase.com/llms-full.txt` (`apps/docs/app/page.tsx`).
## Audience routing (humans vs agents vs crawlers)
Every guide exists in two forms from the same MDX source:
| Form | Produced by | Served to |
| ------------ | -------------------------------------------------------------------- | ------------------------------------ |
| **HTML** | Live MDX render (`partialsRemark`, React components) | Humans in a browser |
| **Markdown** | Prebuild (`generate-guides-markdown.ts` → `public/markdown/guides/`) | Live-fetch agents and bulk downloads |
`apps/docs/middleware.ts` is the bouncer for guide requests. It calls
`negotiateMarkdown()` from `packages/common/markdown-negotiation.ts` and
checks `public/markdown/manifest.json` before serving markdown.
```mermaid
flowchart TD
req[Guide request] --> mdSuffix{URL ends in .md?}
mdSuffix -->|yes| markdown[Serve markdown]
mdSuffix -->|no| agentUA{Live-fetch agent UA?}
agentUA -->|yes| markdown
agentUA -->|no| accept{Accept prefers markdown?}
accept -->|yes| markdown
accept -->|no| html[Serve HTML page]
markdown --> rewrite["Rewrite to /api/guides-md/..."]
rewrite --> file["Read public/markdown/guides/..."]
```
### Who gets what
| Audience | Typical request | Format | Notes |
| ---------------------------------------- | -------------------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------ |
| **Human (browser)** | `/docs/guides/.../nextjs` | HTML | Default — no special headers |
| **Live-fetch agent** | Same URL with `Claude-User`, `ChatGPT-User`, `PerplexityBot`, or `Claude-Web` UA | Markdown | Auto-negotiated even without `.md` suffix |
| **Live-fetch agent** | `/docs/guides/.../nextjs.md` or `Accept: text/markdown` | Markdown | Explicit request |
| **SEO bot** (Googlebot) | Guide URL | HTML | Same page humans see; `isbot()` markdown rewrite applies only to **reference** pages |
| **Training crawler** (GPTBot, ClaudeBot) | Guide URL | HTML | Allowed by `robots.txt` but **not** given alternate markdown — avoids cloaking |
| **Bulk ingest** | `/llms-full.txt`, `/docs/docs.tar.gz` | Markdown | Reads prebuilt `public/markdown/` files |
Humans see interactive UI (copy buttons, styled panels). Agents and bulk
tools read the pre-generated `.md` transcript — not the live HTML DOM.
### Agent discovery
Agents that don't know a specific URL can start from:
- `/llms.txt` — section index with links to `.md` paths
- `/llms-full.txt` — concatenated guides + reference + product overviews
- `rel="alternate" type="text/markdown"` on each guide page
(`GuidesMdx.utils.tsx` → `${BASE_PATH}${pathname}.md`)
- Docs homepage alternate → `https://supabase.com/llms-full.txt`
## Content negotiation
Shared logic lives in `packages/common/markdown-negotiation.ts`, used by
both `apps/docs/middleware.ts` and `apps/www/middleware.ts`.
Markdown is served when:
- The URL ends in `.md`
- `Accept: text/markdown` wins over HTML (q-value comparison)
- The user-agent matches **live-fetch agents**: `Claude-User`,
`Claude-Web`, `ChatGPT-User`, `PerplexityBot`
Training crawlers (`GPTBot`, `ClaudeBot`, etc.) are **not** given
alternate markdown — they are governed by `robots.txt` to avoid
cloaking penalties.
On guides, negotiated requests rewrite to `/api/guides-md/<slug>`.
Each guide page advertises its markdown alternate in metadata via
`GuidesMdx.utils.tsx` (`text/markdown` → `${BASE_PATH}${pathname}.md`).
404 responses from `guides-md` are also markdown and include
`searchDocs` suggestions — useful when an agent fetches a stale URL.
## Bulk vs per-page retrieval
| Strategy | Best for |
| ----------------------------------- | ------------------------------------------------- |
| `/llms.txt` | Discovering available sections and reference libs |
| `/llms-full.txt` | One-shot full ingest |
| `/docs/docs.tar.gz` | Offline/batch ingest of all generated markdown |
| `/docs/guides/<path>.md` | Single guide page |
| `/docs/markdown/reference/<lib>.md` | Single reference lib export |
Reference pages do **not** use the same `Accept: text/markdown`
negotiation as guides. Reference markdown comes from the static export
and `llms-full.txt`, not from middleware negotiation.
## Related
- [`llm-agent-parity.md`](./llm-agent-parity.md) — two-pipeline fidelity,
search caveat, agent onboarding pages, in-flux wiring.
- [`app-map.md`](./app-map.md) — two-pipeline model, directory layout.
- [`build-pipeline.md`](./build-pipeline.md) — prebuild steps that
produce markdown exports and the tarball.
- [`docs-app-direction.md`](./docs-app-direction.md) — one-to-one
markdown fidelity goal.
- [`known-issues.md`](./known-issues.md) — reference page length,
search fragility.
@@ -0,0 +1,103 @@
# Management API reference generation
How `/docs/reference/api/*` is built from the live Management API OpenAPI
spec. Verify against `apps/docs/spec/Makefile` and
`features/docs/Reference.generated.script.ts` before acting — paths drift.
## Pipeline
```mermaid
flowchart LR
live["api.supabase.com<br/>/api/v1-json + v2-json"]
raw["spec/api_v{1,2}_openapi.json"]
redocly["Redocly bundle<br/>(no --dereferenced)"]
bundled["spec/transforms/<br/>api_v*_openapi_deparsed.json"]
sections["common-api-sections.json"]
codegen["codegen:references:legacy"]
gen["features/docs/generated/<br/>api.latest.*.json"]
page["ApiReferencePage →<br/>ApiEndpointSection"]
md["public/markdown/reference/api.md"]
live --> raw --> redocly --> bundled
bundled --> sections
bundled --> codegen
sections --> codegen
codegen --> gen --> page
gen --> md
```
1. **Download** — `make download.api.v1` in `apps/docs/spec/` curls the
live OpenAPI into `api_v1_openapi.json` / `api_v2_openapi.json`.
2. **Bundle** — `make dereference.api.v1` runs Redocly CLI
(`@redocly/cli`) `bundle` into `transforms/*_deparsed.json`.
Management API deliberately omits `--dereferenced` (circular `$ref` in
`APIErrorObject.issues`). `$refs` are resolved later in codegen with a
cycle guard.
3. **Nav sections** — `sections/generateMgmtApiSections.cts` walks
operations/tags into `common-api-sections.json`.
4. **Codegen** — `codegen:references:legacy`
(`Reference.generated.script.ts`) merges v1+v2, resolves refs, writes
`api.latest.endpointsById.json`, `sections.json`, `flat.json`,
`bySlug.json` under `features/docs/generated/`.
5. **Runtime** — `/reference/api/[operation]` via `ApiReferencePage` →
`SectionSwitch` → `ApiEndpointSection` (custom React, not MDX). One
endpoint per page (DOCS-1268). Hand-written intro MDX lives under
`docs/ref/api/`.
6. **Agents** — `generate-reference-markdown.ts` exports the same data to
`public/markdown/reference/api.md`.
Legacy EJS (`generator/api.ts` + `ApiTemplate.ts`) is not the live path.
## Redocly today (bundle / lint)
| Job | Where | Notes |
| ------ | -------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| Bundle | `make dereference.api.v1` (and sibling targets for auth/storage/analytics) | Invoked via `npx --package=@redocly/cli redocly bundle` |
| Lint | `make validate.analytics.v0` only | `redocly lint --extends=minimal`. Management API has no lint target |
Redocly is the OpenAPI **toolchain** (bundle/lint), not the page renderer.
## Rendering: keep the custom path
Pages render via custom React (`ApiEndpointSection`), not Scalar / Redoc /
Stoplight Elements. Reasons (see also [`adding-features.md`](./adding-features.md)
and [`known-issues.md`](./known-issues.md)):
- **Two pipelines** — HTML + markdown export + GraphQL search share one
`IApiEndPoint` / generated-JSON shape. A third-party viewer forks that.
- **Custom extensions** — `x-oauth-scope`, `x-allowed-plans`,
`x-fga-permissions` need first-class rendering.
- **Modular pages** — DOCS-1268 already split the monolithic API page;
embedding a full-spec viewer would reverse that.
- **Surface** — new dep + theming + parity work fights docs-app direction
(reduce surface, don't add parallel renderers).
Prefer improving `ApiEndpointSection` / schema helpers, or deepening
Redocly lint on download/transform. Do not propose swapping the renderer
for an OpenAPI UI kit without addressing markdown/search/`x-*` parity.
Closest CLI substitutes if Redocly were unavailable: `@apidevtools/swagger-parser`
or `swagger-cli` for bundle; Spectral for lint. Switching buys little while
the custom cycle-safe resolve remains required.
## Key files
| Path | Role |
| ------------------------------------------------------- | -------------------------------------------- |
| `apps/docs/spec/Makefile` | download / Redocly bundle / section generate |
| `apps/docs/spec/sections/generateMgmtApiSections.cts` | OpenAPI → `common-api-sections.json` |
| `apps/docs/features/docs/Reference.generated.script.ts` | merge, resolve refs, write `api.latest.*` |
| `apps/docs/features/docs/Reference.api.utils.ts` | `IApiEndPoint` + schema display helpers |
| `apps/docs/features/docs/Reference.apiPage.tsx` | route → one operation page |
| `apps/docs/features/docs/Reference.sections.tsx` | `ApiEndpointSection` UI |
| `apps/docs/internals/generate-reference-markdown.ts` | agent markdown export |
## Related
- [`build-pipeline.md`](./build-pipeline.md) — where `codegen:references`
sits in prebuild.
- [`app-map.md`](./app-map.md) — reference vs guides routing.
- [`known-issues.md`](./known-issues.md) — reference pages avoid standard
MDX; length / modularization direction.
- [`llm-agent-surface.md`](./llm-agent-surface.md) — markdown export
consumption.
+42
View File
@@ -0,0 +1,42 @@
---
name: pm-the-docs
description: >-
Docs-PM decision support for the "Write the docs" authoring process —
makes audience, stage, and cross-cutting scope calls during the Frame
and Shape stages, and helps decide when a docs question needs to
self-serve vs. escalate to a docs PM. Use when framing a new docs page
or launch, deciding what product stage or audience a feature targets,
or judging whether a docs question needs PM sign-off.
---
# PM the docs
Backs the Frame and Shape stages of the "Write the docs" checklist (mirrored in [reference/write-the-docs-checklist.md](reference/write-the-docs-checklist.md)) — the audience, product-stage, and cross-cutting scope calls a docs PM would normally make before drafting starts.
## When to invoke
- Starting a new docs page or launch and need to state the product stage, audience, and "why" before drafting (Frame).
- Deciding content type, IA placement, or prerequisites for a page (Shape).
- Unsure whether a docs question is self-serve or needs a docs PM's sign-off.
**Not for** drafting content itself (see [`write-the-docs`](../write-the-docs/SKILL.md)) or docs-app architecture/IA placement mechanics (see [`ask-the-docs`](../ask-the-docs/SKILL.md)).
## Answering a scope/stage/audience question
1. Read the relevant stage in [reference/write-the-docs-checklist.md](reference/write-the-docs-checklist.md) — its checkboxes state exactly what needs deciding.
2. Read whatever context exists for the feature: the linked issue/project, the PRD, the shipped code or PR. When code and PRD disagree, the code wins for behavior claims.
3. Answer the checklist's questions directly: product stage, audience and job-to-be-done, the one-line "why," content type, IA placement, prerequisites.
4. Distinguish **confirmed fact** (stated in the ticket/PRD/code) from **inference** (your best read) — flag inference explicitly rather than presenting it as settled.
5. If a decision is genuinely open at the org level (not a docs-content call), say so and name who should decide instead of inventing an answer to look complete.
## Self-serve vs. escalate
Self-serve when the checklist is clear, standards exist, and you already know the stage and audience.
Escalate to your docs team's PM when scope or stage is unclear, you need a review path, the bar is ambiguous, or the launch touches cross-cutting surfaces (quickstarts, API keys, tutorials, onboarding, platform concepts) — see the full "Ask the Docs PM" section in the checklist mirror.
## Related skills
- [`ask-the-docs`](../ask-the-docs/SKILL.md) — IA placement and docs-app architecture (Shape stage)
- [`write-the-docs`](../write-the-docs/SKILL.md) — drafting once Frame/Shape are settled
- [`review-the-docs`](../review-the-docs/SKILL.md) — self-review and PR review stages
@@ -0,0 +1,77 @@
# Write the docs
> Mirrors Supabase's proposed "Write the docs" process as of 2026-08-10. Process specifics may still evolve.
A practical six-stage checklist and quality standard for planning, drafting, and reviewing product documentation.
## What good looks like
- The **why** is explicit: a reader learns what problem this solves and when to reach for it, not only the steps.
- The **content type is deliberate** and consistent within the page.
- **Audience and prerequisites** are stated up front.
- At least one **example is runnable and has been run** (commands, code, expected result).
- **Correct stage** is stated; limitations are named honestly.
- The page **lives in the right place** in the IA and links to and from related pages.
- Terminology and formatting match existing docs (defer to the style guide once one lands).
## 1. Frame
_Skills:_ `/ask-the-docs` to see how the surface works today; `/pm-the-docs` for audience, stage, and cross-cutting scope calls.
- [ ] P: State the product stage (private/public alpha, beta, GA)
- [ ] P: Name the audience and the job they are trying to do
- [ ] P: Write one line on _why_ the feature exists (the problem it solves), not only what it does
## 2. Shape
_Skill:_ `/ask-the-docs` for IA placement, architecture, and where content lives.
- [ ] P: Pick the content type(s): tutorial (learning), how-to (a task), reference (lookup), explanation (the why). Do not mix types on one page (refer to [Diátaxis](https://diataxis.fr/))
- [ ] P: Decide where the page lives in the existing IA and what links in and out (avoid orphan pages)
- [ ] P: List prerequisites and assumed knowledge up front
## 3. Draft
_Skill:_ `/write-the-docs` to draft net-new content grounded in Linear and the code.
- [ ] P: Lead with the why and the outcome, then the how/what (product story first)
- [ ] P: Include at least one runnable, copy-pasteable example that you have actually run
- [ ] E: Contribute technical depth and verify accuracy (APIs, limits, edge cases)
- [ ] P: Call out the current stage inline and any known limitations
## 4. Self-review against the bar
_Skill:_ `/review-the-docs` — [Local self-review](../review-the-docs/SKILL.md#local-self-review-no-open-pr) on your own branch before opening the PR.
- [ ] P/E: Check the draft against "What good looks like" above before opening the PR
- [ ] P/E: Follow authoring-experience standards and tooling when available
## 5. PR review
_Skill:_ `/review-the-docs` to triage, classify, verify the build, and report.
- [ ] P/E: Open the PR and request review per the rules of engagement
- [ ] Docs: Review against the published bar
## 6. Keep it honest
- [ ] P: Keep the product launch checklist's "start on day 1" docs gate honest through ship (update as stage or behavior changes)
## Ask the Docs PM
**Self-serve when:** the checklist above is clear, standards exist, and you know the product stage and audience.
**Ask when:** scope or stage is unclear, you need a review path, the bar is ambiguous, or the launch docs touch cross-cutting surfaces (quickstarts, API keys, tutorials, onboarding, platform concepts).
**What to expect:** the docs PM is the point of contact for questions and review against the bar.
**Where to ping:** your team's PR-review channel and current docs PM — check your contributor guide for who that is today.
## Reference
Role prefixes:
- **P** = Product. The product lead / area PM who writes the docs.
- **E** = Engineering. Contributes technical depth and verifies accuracy.
- **P/E** = Product and Engineering together.
- **Docs** = Docs team, the reviewer.
+404
View File
@@ -0,0 +1,404 @@
---
name: review-the-docs
description: >-
Review Supabase docs changes locally in ~/GitHub/supabase/supabase —
either an open PR (triage, classify, verify) or your own branch before
opening a PR (local self-review). Covers markdown pipeline, MDX content,
tutorials, examples, Studio links, and docs tooling. Use when asked to
review docs PRs, self-review a draft branch, check who has approved,
verify build output, or evaluate supabase/supabase documentation changes.
---
# Review docs PRs
Local review workflow for `supabase/supabase` docs changes. Classify first, then follow the matching checklist.
Two modes:
- **Open PR review** (default) — triage via `gh`, checkout, verify, report. Start at [Phase 1](#phase-1--triage-read-only).
- **Local self-review** — no open PR yet; verify the current branch before opening one. Start at [Local self-review](#local-self-review-no-open-pr).
For **implementing** docs fixes (Linear tickets, worktrees, platform E2E), use [`work-linear-issue`](https://github.com/supabase/docs-agent-skills/blob/main/.claude/skills/work-linear-issue/SKILL.md) instead.
## Core rules
1. **Classify before reviewing** — path patterns determine which checklist applies.
2. **Review stacked PRs bottom-up** — each PR may base on the previous branch.
3. **Run verification locally** — do not approve from diff alone.
4. **Compare against `master`** when the PR claims to fix missing or broken output.
5. **One report per batch** — sequential review, consolidated output at the end.
6. **Separate blockers from nits** — type/style notes are suggestions unless output breaks.
## Repository layout
| Path | Purpose |
| -------------------------------------- | --------------------------------------------------------- |
| `~/GitHub/supabase/supabase` | Main clone for review checkouts |
| `apps/docs/content/guides/` | Source MDX |
| `apps/docs/internals/` | Markdown pipeline (`generate-guides-markdown.ts`, etc.) |
| `apps/docs/internals/markdown-schema/` | Component handlers → plain markdown strings |
| `apps/docs/public/markdown/guides/` | Generated output (produced by build) |
| `apps/docs/components/` | React MDX components |
| `examples/` | Tutorial/quickstart apps referenced via `$CodeSample` |
| `apps/studio/` | Dashboard UI; may link to hosted docs |
| `.agents/skills/` | In-repo agent skills (symlinked from `.claude`/`.cursor`) |
## Local self-review (no open PR)
Use this on your own branch **before** opening a PR (checklist Stage 4). No `gh pr` required.
```bash
cd ~/GitHub/supabase/supabase
# Ensure you're on the feature branch, not master
git branch --show-current
git diff --name-only master...HEAD
```
1. **Classify** from `git diff --name-only master...HEAD` using the [Phase 2](#phase-2--classify-pr-type) table.
2. **Walk the bar** in [`pm-the-docs`'s checklist](../pm-the-docs/reference/write-the-docs-checklist.md) — "What good looks like" and the Self-review checkboxes.
3. **Run type-specific checks** from the matching sections below on the current branch (no checkout step). Typical commands:
```bash
# Content / tutorial MDX
cd apps/docs && pnpm lint:mdx -- <changed-paths>
# Pipeline / schema handler
cd apps/docs && pnpm build:guides-markdown
# inspect public/markdown/guides/ for affected pages
pnpm build:reference-markdown # when reference pipeline changed
```
4. Spot-check frontmatter, internal links, and nav wiring for content changes.
5. Write a short **self-review note** (blockers vs nits) suitable to paste into the future PR body under a "Self-review" heading.
Then open the PR and continue with open-PR review if a second pass is needed.
## Phase 1 — Triage (read-only)
### List PRs
Filter by author, label, or list all open docs PRs:
```bash
# By author
gh pr list --repo supabase/supabase --author <github-user> --state open \
--json number,title,url,reviewDecision,latestReviews,changedFiles,additions,deletions,labels
# All open docs-labeled PRs
gh pr list --repo supabase/supabase --state open --label documentation \
--json number,title,url,reviewDecision,latestReviews,author,changedFiles
```
PRs with empty `reviewDecision` and no `APPROVED` review need approval.
### Map the stack
```bash
gh pr view <number> --repo supabase/supabase \
--json number,title,baseRefName,headRefName,body,files
```
Stacked series: `master → PR A → PR B → PR C`. Review and merge bottom-up.
## Phase 2 — Classify PR type
Inspect changed files from `gh pr view` or:
```bash
gh pr diff <number> --repo supabase/supabase --name-only
```
| PR type | Path signals | Primary skill section |
| --------------------------- | --------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
| **Markdown-schema handler** | `apps/docs/internals/markdown-schema/`, `generate-guides-markdown.ts` | [Schema handler review](#schema-handler-review) |
| **Pipeline / internals** | `apps/docs/internals/` (not just one new handler) | [Pipeline review](#pipeline-review) |
| **Content-only MDX** | `apps/docs/content/**` only | [Content review](#content-review) |
| **Tutorial / quickstart** | `apps/docs/content/guides/**/tutorials/`, `quickstarts/`, plus `examples/` | [Tutorial review](#tutorial-review) → also `work-linear-issue` |
| **Example app only** | `examples/**` without matching MDX | [Example review](#example-review) |
| **Studio ↔ docs links** | `apps/studio/**` | [Studio review](#studio-review) |
| **Docs UI / components** | `apps/docs/components/`, `apps/docs/features/` (no pipeline) | [Component review](#component-review) |
| **Docs tooling** | `.agents/skills/`, `.claude/skills/`, `.cursor/skills/`, `apps/docs/CONTRIBUTING.md`, `apps/docs/DEVELOPERS.md` | [Docs tooling review](#docs-tooling-review) |
| **Mixed** | Multiple path groups above | Run each applicable section; note overlap |
When a PR spans types (e.g. schema handler + component refactor), run **all** matching sections.
## Phase 3 — Sequential local review
Repeat for **each PR** (bottom of stack first).
### Common steps (all PR types)
**Checkout and install:**
```bash
cd ~/GitHub/supabase/supabase
gh pr checkout <number> --repo supabase/supabase
pnpm install --filter docs... # when node_modules missing or deps changed
```
**CI spot-check:**
```bash
gh pr checks <number> --repo supabase/supabase
```
**Baseline on master** (when PR fixes missing/broken output):
```bash
git checkout master
# run type-specific verify command (see sections below)
git checkout - # return to PR branch
```
---
### Schema handler review
For PRs adding static markdown fallbacks for React MDX components.
**Code checks** — each handler in `apps/docs/internals/markdown-schema/`:
| Check | What to verify |
| ---------------- | ------------------------------------------------------------------------ |
| Data source | Same data/constants as the React component — no duplicated config |
| CJS interop | `shared-data` via `createRequire(import.meta.url)` (see `SharedData.ts`) |
| Local JSON | Direct imports fine for `apps/docs/data/` |
| Link prefix | Links use `withDocsBasePath` |
| SCHEMA wiring | Registered in `SCHEMA` in `generate-guides-markdown.ts` |
| Props / shapes | All MDX usages covered — flat arrays and `{ items: [...] }` sections |
| Silent fallbacks | `''` for unknown props OK if consistent with existing handlers |
Find usages: `rg '<ComponentName' apps/docs/content/`
**Build and inspect:**
```bash
cd apps/docs && pnpm build:guides-markdown
# Expect: Generated 546 markdown files under public/markdown/guides/
```
Inspect `public/markdown/guides/` for affected pages:
- Previously blank sections now have lists, tables, or links
- All MDX pages using the component are covered, not just the one in the PR description
- Link format: `/docs/guides/...` locally; absolute URLs when `VERCEL_ENV=production`
---
### Pipeline review
For AST refactors, link rewriting, reference markdown generation, etc.
```bash
cd apps/docs
pnpm build:guides-markdown
pnpm build:reference-markdown # when reference pipeline changed
pnpm test internals/internal-links.test.ts # when link handling changed
```
Verify both guides and reference output when `generate-reference-markdown.ts` or `internal-links.ts` changed.
---
### Content review
MDX prose, partials, navigation — no pipeline or example changes.
```bash
cd apps/docs
pnpm lint:mdx -- <changed-paths> # or monorepo equivalent on changed files
```
Checklist:
- [ ] Frontmatter valid (`title`, `description` where required)
- [ ] Internal links resolve (`/docs/guides/...`, not broken anchors)
- [ ] `$CodeSample` paths match existing example directories
- [ ] Admonitions, tabs, and partial includes render sensibly in PR preview
- [ ] No accidental whitespace-only or empty sections where components were removed
Compare PR preview URL (from Vercel/deployment comment) against production for visual regressions when layout components are involved.
---
### Tutorial review
Tutorial MDX plus matching example app. **Read [`work-linear-issue`](https://github.com/supabase/docs-agent-skills/blob/main/.claude/skills/work-linear-issue/SKILL.md)** for full platform E2E — review is not complete without it when auth flows are involved.
```bash
# MDX lint
cd apps/docs && pnpm lint:mdx -- content/guides/getting-started/tutorials/<path>
# Example build (from work-linear-issue)
cd examples/<example-dir>
npm install && npm run build
```
Checklist:
- [ ] MDX steps match example code after `pnpm codegen:examples` (if `$CodeSample` used)
- [ ] Env var names and Supabase client setup match current `@supabase/ssr` patterns
- [ ] Example pins catalog versions — no `"latest"` for in-repo packages
- [ ] **Platform E2E** (when auth involved): SQL migration applied, auth flow walked, profiles verified — see `work-linear-issue` Phase 3
---
### Example review
Example-only PRs (or example portion of a tutorial PR).
```bash
cd examples/<example-dir>
npm install && npm run build
```
Checklist:
- [ ] Build passes with no type errors
- [ ] `.env.example` documents required vars (no secrets committed)
- [ ] If docs reference this example, `$CodeSample` paths still valid
---
### Studio review
Dashboard changes linking to docs.
Checklist:
- [ ] Links point to hosted docs anchors (e.g. `/guides/auth/auth-email-templates#terminology`)
- [ ] Local-dev-only doc paths not used as the sole link target
- [ ] Link text matches the destination section
---
### Component review
React component changes under `apps/docs/components/` without a new schema handler.
Checklist:
- [ ] No browser-only APIs leaked into build-script imports
- [ ] Shared constants extracted cleanly when also consumed by markdown handlers
- [ ] Visual behavior unchanged or intentionally improved — check PR screenshots
- [ ] If component is used in MDX exported to markdown, confirm a schema handler exists or file an follow-up
---
### Docs tooling review
Agent skills, contributor docs, or skill symlink wiring — no MDX/pipeline changes required.
Checklist:
- [ ] Symlinks under `.claude/skills/` and `.cursor/skills/` resolve to `.agents/skills/...` (same pattern as `vitest`)
- [ ] Cross-skill links resolve: relative for in-repo skills; absolute `docs-agent-skills` URLs only for skills that remain in that private repo
- [ ] No personal vault paths, Obsidian references, or private-process-only instructions
- [ ] `apps/docs/CONTRIBUTING.md` / `DEVELOPERS.md` pointers match skill names and checklist stages
- [ ] Reference files under a skill stay near the ~250-line guideline (split if bloated)
```bash
# Symlink smoke check
ls -la .claude/skills/<skill-name> .cursor/skills/<skill-name>
test -f .claude/skills/<skill-name>/SKILL.md
# Leftover internal refs
rg -n 'Obsidian|pm-the-docs-full|Priorities/' .agents/skills
```
---
## Phase 4 — Review report
One consolidated report after all PRs are reviewed.
### Report template
```markdown
# PR review report — <author, label, or topic>
Reviewed locally at `~/GitHub/supabase/supabase`.
**Stack order:** master → #NNN → … (if applicable)
---
## [#NNN — Title](https://github.com/supabase/supabase/pull/NNN)
**Type:** schema handler | pipeline | content | tutorial | example | studio | component | docs tooling | mixed
**Verdict:** Approve | Approve with nits | Request changes
| Check | Result |
| ------------------ | ------ |
| PR type checks | … |
| Build / lint | … |
| Baseline vs master | … |
| CI | … |
**Verified:**
- …
**Notes:**
- …
---
## Summary
| PR | Type | Recommendation | Blockers |
| ---- | ---- | -------------- | -------- |
| #NNN | … | … | … |
**Merge order:** bottom-up after approval (if stacked).
```
### Verdict guidance
| Verdict | When |
| --------------------- | ------------------------------------------------------------------------------------ |
| **Approve** | All type-specific checks pass; output correct |
| **Approve with nits** | Works correctly; minor type/style/docs nits only |
| **Request changes** | Build/lint fails, broken links, wrong data, missing coverage, or failed platform E2E |
## Inline review comments
```text
https://github.com/supabase/supabase/pull/<number>/files#diff-<blob-sha>R<line>
```
```bash
gh api repos/supabase/supabase/pulls/<number>/files \
--jq '.[] | select(.filename | endswith("<file>")) | .sha'
```
Include concrete evidence — JSON line numbers, before/after output snippets, failing command output.
## Handler pattern reference
```typescript
// apps/docs/internals/markdown-schema/Example.ts
import { withDocsBasePath } from '../internal-links'
export const Example = ({ props }: { props: Record<string, unknown> }): string => {
// Same data source as React component → plain markdown string
}
```
## Parallel work
Independent PRs: subagents can review in separate worktrees. **Stacked** series: review sequentially on one clone, bottom-up.
## Output checklist
- [ ] Approval status fetched for all requested PRs
- [ ] Each PR classified by type
- [ ] Stack order documented (if applicable)
- [ ] Type-specific verification run locally (not just schema handler defaults)
- [ ] Master baseline compared when PR fixes missing output
- [ ] Platform E2E noted for tutorial/auth PRs (or deferred with reason)
- [ ] Verdict and blockers stated per PR
- [ ] Merge order recommended
- [ ] Inline comment links provided for nits
+78
View File
@@ -0,0 +1,78 @@
---
name: write-the-docs
description: >-
Draft new or updated Supabase docs content for a feature or launch,
grounded in Linear (the ticket plus its product/PM context), a read of the
actual code, and the docs style guide once one exists. Use when asked to
write docs for a new feature, a launch (e.g. Select 2026), or a Linear
ticket that needs net-new content rather than a bug fix. Not for
implementing existing docs bug reports — use work-linear-issue for that.
---
# Write the docs
Drafts net-new (or substantially rewritten) Supabase docs content for a feature or launch. Distinct from [`work-linear-issue`](https://github.com/supabase/docs-agent-skills/blob/main/.claude/skills/work-linear-issue/SKILL.md), which implements and fixes existing docs tickets — this skill is for the case where the content doesn't exist yet and has to be authored from scratch, grounded in four inputs rather than guessed.
## Core rules
1. **Gather before drafting.** Never draft from a ticket title alone. Pull all four inputs below first; a thin gather phase produces a draft that's wrong about how the feature actually works.
2. **Separate confirmed behavior from product intent from inference.** Code tells you what the feature does today. Linear/PRD/PRFAQ tells you what it's meant to do and how it should be positioned. Anything you had to guess, flag explicitly rather than stating it as fact.
3. **Follow CONTRIBUTING.md and WORD_LIST.md; say so when you fall back to a precedent page.** Don't silently invent voice/structure rules — name the nearest existing-page precedent you followed instead (see [reference/style-fallback.md](reference/style-fallback.md)).
4. **Reuse, don't duplicate.** For docs-app architecture/placement questions, use [`ask-the-docs`](../ask-the-docs/SKILL.md) and [`audit-docs-ia`](https://github.com/supabase/docs-agent-skills/blob/main/.claude/skills/audit-docs-ia/SKILL.md) rather than re-deriving that knowledge here.
5. **Know what you're actually drafting.** Not everything that looks like "docs for a feature" is a hand-written page — see the content-type gate below before you start writing.
## Phase 1 — Gather (read-only)
Four inputs, in order:
1. **Style guide.** Start with [`apps/docs/CONTRIBUTING.md`](../../../apps/docs/CONTRIBUTING.md) and [`apps/docs/WORD_LIST.md`](../../../apps/docs/WORD_LIST.md) for voice, structure, and terminology. If those don't cover the case, fall back to the nearest comparable existing page under `apps/docs/content/` and say explicitly: _"no dedicated style guide yet — following the precedent of `<page>`."_ See [reference/style-fallback.md](reference/style-fallback.md).
2. **Linear — the ticket and its product context.** Pull the Linear issue itself, then don't stop there: pull its parent project/initiative description too (PRD, PRFAQ, RFC, or initiative narrative) and any PM comments. Product framing/positioning language usually lives one level up from the ticket, not in the ticket body — see how the Select 2026 initiative's own description carried the real launch narrative, not any single project's ticket. Distinguish scope the ticket actually commits to from aspirational language in the PRD.
3. **Code.** Read the actual implementation before writing a single behavior claim — the PRD describes intent, the code describes what shipped. Check the Linear issue/project first for a linked `supabase/supabase` PR — its diff and description are the most precise "what actually shipped" source, more precise than a general codebase read. If no PR is linked, locate the feature directly in `supabase/supabase` (or the product's own repo), and apply [`ask-the-docs`](../ask-the-docs/SKILL.md)'s reuse/minimalism lens: understand what exists before describing it. If code and PRD disagree, the code wins for behavior claims — flag the mismatch rather than silently picking one.
4. **Whatever else the author supplies.** Screenshots, example projects, related pages, Slack threads, a specific voice sample. Screenshots are for more than general context — use them to verify the _exact_ button/menu/field labels before writing instructional steps that reference them; a mismatched UI label is one of the easiest, most avoidable errors in a draft. Ask for these when the feature's user-facing shape is still unclear after 1–3, rather than guessing.
Summarize all four back to the requester before drafting: what's confirmed, what's product intent vs. shipped behavior, what's still a gap. Stop and ask if a real gap would change the draft's structure or scope.
## Phase 1.5 — Content-type gate
Before drafting, classify what's actually being asked for against `apps/docs`'s real content types (see [`ask-the-docs`](../ask-the-docs/SKILL.md)'s `app-map.md` "Content types" table, and [reference/content-type-gate.md](reference/content-type-gate.md) here):
- **Guide / tutorial** — hand-written MDX under `content/guides/`. This is what this skill drafts.
- **Troubleshooting** — hand-written MDX under `content/troubleshooting/`, sometimes synced from GitHub issues. Also in scope.
- **Reference** — generated from `spec/` (OpenAPI, SDK YAML, CLI config) → `features/docs/generated/**`. **Not hand-authored via the standard MDX path.** If the ask is actually reference-type content (a new API endpoint, config option, or SDK method that needs a reference entry), stop drafting MDX — it would diverge from or get silently overwritten by the generator. Instead point to the spec/codegen pipeline (`apps/docs/spec/`, `apps/docs/generator/`; see `ask-the-docs`'s `management-api-reference.md` for the OpenAPI-specific flow) and say so explicitly rather than producing a page that looks done but isn't the real fix.
When in doubt, ask `ask-the-docs` rather than guessing — this classification is the one call in this skill most likely to be wrong if made from outside knowledge of the app.
## Phase 2 — Draft
- Follow `apps/docs` MDX conventions (component usage, frontmatter, code sample wiring) — see [`ask-the-docs`](../ask-the-docs/SKILL.md) for the pipeline details rather than re-deriving them.
- Place the page using existing IA precedent; for a placement call that isn't obvious, consult [`audit-docs-ia`](https://github.com/supabase/docs-agent-skills/blob/main/.claude/skills/audit-docs-ia/SKILL.md)'s nav/IA knowledge rather than guessing a nav slot.
- **Wire it into navigation, not just onto disk.** Placement (which section) and nav enablement (whether it actually shows up) are separate — confirm the current nav-registration mechanism via `ask-the-docs`/`audit-docs-ia` rather than assuming a page is discoverable just because the file exists in the right folder.
- Ground every behavior claim in Phase 1's code read (the linked PR when there is one); ground every "why this matters" framing in the PRD/PM context; mark inferred material inline (e.g. an HTML comment or a flagged line in the handoff summary) so a reviewer can find it fast.
## Phase 2.5 — Review checklist
Before handing off, confirm:
- [ ] CONTRIBUTING.md / WORD_LIST.md followed, or precedent page named explicitly
- [ ] Every behavior claim traces to the code read (ideally the linked PR), not just the PRD
- [ ] Every "why it matters" / positioning line traces to Linear/PM context, not invented
- [ ] Inferred or assumed material is flagged, not stated as fact
- [ ] Content type confirmed as Guide/Troubleshooting (not something that belongs in generated Reference instead)
- [ ] Nav placement and nav enablement both wired, not just the placement
- [ ] Internal links resolve; first-use of new terms/acronyms is defined
## Phase 3 — Handoff
This skill stops at a reviewable draft. It does not open worktrees or PRs itself:
- Hand off to [`create-pull-request`](https://github.com/supabase/docs-agent-skills/blob/main/.claude/skills/create-pull-request/SKILL.md) (and [`work-linear-issue`](https://github.com/supabase/docs-agent-skills/blob/main/.claude/skills/work-linear-issue/SKILL.md) if the ticket needs a full worktree+PR flow) for the actual PR mechanics. Carry the Phase 1/2 flagged-assumptions list forward explicitly into that handoff — it belongs in the PR description (e.g. a "needs review" section) so a reviewer sees it, not just as an inline comment buried in the draft.
- If the feature is UI-driven and the PR will need screenshots/GIFs, flag [`proof-it-works`](https://github.com/supabase/docs-agent-skills/blob/main/.claude/skills/proof-it-works/SKILL.md) as the next step rather than capturing evidence here.
## Additional resources
- Style / terminology: [`apps/docs/CONTRIBUTING.md`](../../../apps/docs/CONTRIBUTING.md), [`apps/docs/WORD_LIST.md`](../../../apps/docs/WORD_LIST.md), [reference/style-fallback.md](reference/style-fallback.md)
- Content-type gate detail: [reference/content-type-gate.md](reference/content-type-gate.md)
- "Write the docs" checklist (Draft stage): [`pm-the-docs`](../pm-the-docs/SKILL.md)'s [reference/write-the-docs-checklist.md](../pm-the-docs/reference/write-the-docs-checklist.md)
- Docs-app architecture/placement: [`ask-the-docs`](../ask-the-docs/SKILL.md), [`audit-docs-ia`](https://github.com/supabase/docs-agent-skills/blob/main/.claude/skills/audit-docs-ia/SKILL.md)
- PR mechanics: [`create-pull-request`](https://github.com/supabase/docs-agent-skills/blob/main/.claude/skills/create-pull-request/SKILL.md), [`work-linear-issue`](https://github.com/supabase/docs-agent-skills/blob/main/.claude/skills/work-linear-issue/SKILL.md)
- Screenshots/proof: [`proof-it-works`](https://github.com/supabase/docs-agent-skills/blob/main/.claude/skills/proof-it-works/SKILL.md)
@@ -0,0 +1,22 @@
# Content-type gate
Before drafting anything, classify the request against `apps/docs`'s real content types. This is grounded in `ask-the-docs`'s `app-map.md` "Content types" table — re-check that table if this note and the live app-map ever disagree, since the app-map is the source of truth.
| Type | Location | Hand-authored by this skill? |
|---|---|---|
| **Guide / tutorial** | `content/guides/` | Yes — hand-written MDX, goal-oriented. This is the default case. |
| **Troubleshooting** | `content/troubleshooting/` | Yes, though some entries sync from GitHub issues — check before assuming a fresh page is needed. |
| **Reference** | Generated from `spec/` (OpenAPI, SDK YAML, CLI config) → `features/docs/generated/**` | **No.** Reference pages do not use the standard MDX path — see `ask-the-docs`'s `docs-app-direction.md` for why, and `management-api-reference.md` for the OpenAPI-specific spec → codegen → reference-page flow. |
| **Federated** | External repos, pulled at build time | No — out of scope for this skill; see `ask-the-docs`'s `federated-docs.md`. |
## What to do when the ask is actually Reference-type
A common trap: a ticket says "document the new `X` config option" or "add docs for the new API endpoint," which sounds like a normal doc-writing ask but is actually a spec change. If so:
1. Don't hand-draft an MDX reference page — it will diverge from or get silently overwritten by the next `generate-reference-markdown.ts` run.
2. Identify the correct spec source (`apps/docs/spec/` — OpenAPI, SDKSpec, ConfigSpec, or CLISpec depending on the surface).
3. Say explicitly in the handoff that the real fix is a spec/codegen change, not a docs PR from this skill, and point to `ask-the-docs` for the specific spec-editing workflow.
## When it's genuinely ambiguous
Some features span both — e.g. a new API endpoint (Reference) that also needs a task-oriented guide showing how to use it (Guide). In that case, split the work: flag the Reference-type piece per above, and draft only the Guide-type piece here.
@@ -0,0 +1,18 @@
# Style guide fallback
There is no separate published style guide yet beyond what already lives in
this repo. Use the public sources below, in order:
1. Read [`apps/docs/CONTRIBUTING.md`](../../../../apps/docs/CONTRIBUTING.md)
for authoring conventions (voice, structure, document types).
2. Read [`apps/docs/WORD_LIST.md`](../../../../apps/docs/WORD_LIST.md) for
preferred spelling, capitalization, and terminology.
3. If those don't cover the case, find the nearest comparable existing page
under `apps/docs/content/` (same product area, similar content type —
reference vs. guide vs. quickstart) and follow its voice, heading
structure, and code-sample conventions.
4. State which page you followed as precedent in the handoff summary — e.g.
"no dedicated style guide yet — following the precedent of
`guides/storage/uploads.mdx`."
5. When a dedicated style guide is added to the repo later, prefer it over
precedent-matching and drop this fallback step.
+1
View File
@@ -0,0 +1 @@
../../.agents/skills/ask-the-docs
+1
View File
@@ -0,0 +1 @@
../../.agents/skills/pm-the-docs
+1
View File
@@ -0,0 +1 @@
../../.agents/skills/review-the-docs
+1
View File
@@ -0,0 +1 @@
../../.agents/skills/write-the-docs
+1
View File
@@ -0,0 +1 @@
../../.agents/skills/ask-the-docs
+1
View File
@@ -0,0 +1 @@
../../.agents/skills/pm-the-docs
+1
View File
@@ -0,0 +1 @@
../../.agents/skills/review-the-docs
+1
View File
@@ -0,0 +1 @@
../../.agents/skills/write-the-docs
+15
View File
@@ -19,6 +19,21 @@ To make docs as clear as possible:
- Avoid using idioms and colloquialisms, such as `piece of cake`. These phrases are often specific to a region or culture.
- Refer to the reader as `you`. Don't use `we` to refer to the reader. Use `we` only to refer to the Supabase team.
## AI agent skills for docs authoring
If you're using Claude Code or Cursor, this repo ships four skills that back the [Write the docs](../../.agents/skills/pm-the-docs/reference/write-the-docs-checklist.md) authoring checklist.
Invoke a skill by name: `/write-the-docs`, `/ask-the-docs`, `/pm-the-docs`, `/review-the-docs`.
| Skill | Checklist stage | Use for |
| --- | --- | --- |
| [`pm-the-docs`](../../.agents/skills/pm-the-docs/SKILL.md) | Frame / Shape | Audience, product-stage, and cross-cutting scope calls |
| [`ask-the-docs`](../../.agents/skills/ask-the-docs/SKILL.md) | Frame / Shape | `apps/docs` architecture, IA placement, and where content lives |
| [`write-the-docs`](../../.agents/skills/write-the-docs/SKILL.md) | Draft | Drafting net-new content grounded in the code |
| [`review-the-docs`](../../.agents/skills/review-the-docs/SKILL.md) | Self-review / PR review | Checking a draft and PR triage/verification |
The canonical files live in `.agents/skills/`, with Git symlinks in `.claude/skills/` and `.cursor/skills/`.
## Document types
Supabase docs contain 4 types of documents. Before you start writing, think about what type of doc you need.