Skip to content
Merged
8 changes: 8 additions & 0 deletions .changelog/unreleased/640-dx1-content-batch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
type: added
area: docs
pr: 640
breaking: false
---

Add the documentation contribution guide, page templates, and PR template; the docs home page; and pages for oracles, pools, earn, referrals, and the faucet.
56 changes: 56 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE/documentation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<!--
Documentation PR template. Use it by adding ?template=documentation.md to the
compare URL, or picking "documentation" from the template dropdown.
Guide: apps/docs/CONTRIBUTING.md · Spec: docs/dx_1/003_content_map.md §4
-->

## Page(s)

<!-- Route and file for each page, e.g. /guides/pools — apps/docs/content/guides/pools.mdx -->

- Route: `/`
File: `apps/docs/content/`
Issue: closes #

## Reader

<!-- Which of the three readers from content map §1 is this page for? Trader, liquidity provider, or integrator/contributor. One primary reader per page. -->

Primary reader:

## Sources

<!--
Every claim about protocol mechanics must name the file it came from.
One row per claim. "The faucet cooldown surfaces as contract error code 6"
→ apps/web/src/features/faucet/hooks/useClaim.tsx
-->

| Claim in the page | Source file |
| ----------------- | ----------- |
| | |

## Worked example

<!-- Paste the inputs, the arithmetic, and the result. The numbers must have been computed, not estimated. If the page has no worked example, say why it does not need one. -->

## Definition of done (content map §4)

- [ ] The page exists at its mapped path with valid frontmatter (`title` ≤ 60, `description` 50–160, real `updated:` date, valid `status`).
- [ ] `bun run --cwd apps/docs check:content` passes.
- [ ] `bun run --cwd apps/docs check:links` passes.
- [ ] `bun run --cwd apps/docs lint:prose` passes with no new warnings.
- [ ] Every mechanical claim above names its source file.
- [ ] The worked example's numbers were computed, not estimated.
- [ ] The page is listed in `apps/docs/content/meta.json` in content-map order (or, for `/index`, is intentionally excluded).
- [ ] Reading level fits the target reader.
- [ ] Renders correctly in light and dark themes at a mobile and a desktop width.
- [ ] For guides: every documented action was performed by the author on testnet — confirmed here:

## Anything unverified

<!-- List any claim a maintainer still needs to confirm, and set the page's status to `draft` until they have. "None" is a valid answer. -->

## Changelog

- [ ] Added `.changelog/unreleased/<pr>-<slug>.md` with `area: docs` (see `.changelog/unreleased/README.md`).
3 changes: 3 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,9 @@ Violating these silently breaks the build for everyone.
- **Docs content is validated centrally.** Run
`bun run --cwd apps/docs check:content` or the root `bun run check:content`
before changing docs content, manifests, or docs assets.
[`apps/docs/CONTRIBUTING.md`](./apps/docs/CONTRIBUTING.md) is the contract for
documentation pull requests: the frontmatter rules, the manifest, the concept
/ guide / reference page templates, and the checks that gate them.

---

Expand Down
11 changes: 8 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,14 @@ even when your fork has drifted behind.
- Prefer workspace imports (`@workspace/ui/...`) over deep relative paths.
- Use design tokens, not raw values — `check:tokens` enforces this. Text sizes
and radii have named tokens in `packages/ui/src/styles/globals.css`.
- For docs content, run `bun run --cwd apps/docs check:content`. Use
`bun run --cwd apps/docs check:content -- --fix` only for the mechanically
fixable subset: `updated:` dates and manifest ordering.
- For docs content, follow
[`apps/docs/CONTRIBUTING.md`](./apps/docs/CONTRIBUTING.md) — it covers the
frontmatter contract, the `content/meta.json` manifest, the concept / guide /
reference page templates, and the
[documentation PR template](./.github/PULL_REQUEST_TEMPLATE/documentation.md).
Run `bun run --cwd apps/docs check:content`, `check:links`, and `lint:prose`;
`check:content -- --fix` only mechanically fixes `updated:` dates and manifest
ordering.
- Comment non-obvious intent only. Don't restate the code.

### 3. Format
Expand Down
162 changes: 162 additions & 0 deletions apps/docs/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
# Contributing to the SO4 documentation

This guide covers documentation content only — MDX pages under
`apps/docs/content/`. For code, the toolchain, and the commit gate, read the
root [`CONTRIBUTING.md`](../../CONTRIBUTING.md) and [`AGENTS.md`](../../AGENTS.md).

The information architecture, reader personas, and page contract this guide
enforces are defined in
[`docs/dx_1/003_content_map.md`](../../docs/dx_1/003_content_map.md). This file
makes those rules runnable; the content map is the source of truth if the two
ever disagree.

---

## Where content lives

```
apps/docs/
content/
index.mdx docs home (exempt from the sidebar; see meta.json note)
get-started/*.mdx
concepts/*.mdx
guides/*.mdx
developers/*.mdx
reference/*.mdx includes *.generated.mdx — do not hand-edit those
resources/*.mdx
meta.json the sidebar manifest — every page except /index is listed here
templates/ copy-me starting points: concept.mdx, guide.mdx, reference.mdx
PROSE_STYLE.md the full voice guide; prose lint enforces a subset
REVIEWER_CHECKLIST.md what a reviewer checks before approving
scripts/ check-content.ts, check-links.ts, lint-prose.ts
```

A page's route is its path under `content/` with `.mdx` removed:
`content/guides/pools.mdx` serves at `/guides/pools`.

## The frontmatter contract

Every page starts with a fenced frontmatter block. The parser
(`scripts/content.ts`) is line-based `key: value` — no nested keys, no
multi-line values, no comments.

```mdx
---
title: Pools
description: Adding and removing liquidity in an SO4 GM pool, what a deposit is exposed to, and how to exit a position.
updated: 2026-08-31
status: stable
---
```

| Key | Rule (`scripts/check-content.ts`, `src/lib/frontmatter.ts`) |
| ------------- | ---------------------------------------------------------------------- |
| `title` | 1–60 characters. Appears in the sidebar and the browser tab. |
| `description` | 50–160 characters. Used for search and social cards. |
| `updated` | `YYYY-MM-DD`. Set it to the date you finished the page. |
| `status` | `stable`, `beta`, or `draft`. Use `draft` if a maintainer still needs to verify claims. |

Optional keys the schema allows: `sidebarLabel`, `order`, `tags`.

## Adding a page to the manifest

`content/meta.json` is the sidebar. A page that is not listed there fails
`check:content` with `orphan page /your/route`, and a listed page that does not
exist fails with `sidebar references missing /your/route`.

Add your route (path without the leading slash) to the right section's `pages`
array, in the order it appears in the content map §2:

```json
{
"label": "Guides",
"pages": ["guides/pools", "guides/earn", "guides/referrals", "guides/faucet", "guides/troubleshooting"]
}
```

`/index` is the one deliberate exception — it is the home page and is not in
the sidebar.

## The page contract (content map §3)

Every page:

1. **Answers its title question in the first paragraph**, before any scrolling.
2. **Carries one worked example with real numbers you computed**, not estimated.
"A 10x long on 100 USDC liquidates at roughly a 9.1% adverse move" is
checkable; "leverage is risky" is not.
3. **Links to adjacent concepts inline**, where the reader needs them — not in a
trailing "see also" list.
4. **States a true `updated:` date.**

And avoids screenshots of weekly-changing UI, numbers that will silently rot
(fees, addresses, schema — those come from the `*.generated.mdx` reference
pages), and second-person imperative stacked ten deep.

## Which checks to run

From the repository root, or with `--cwd apps/docs`:

```bash
bun run --cwd apps/docs check:content # frontmatter, manifest, orphans, alt text, glossary
bun run --cwd apps/docs check:links # internal links and heading anchors resolve
bun run --cwd apps/docs lint:prose # banned words, capitalisation, exclamation marks
bun run --cwd apps/docs check:faq # only if you touched resources/faq.mdx
```

`bun run --cwd apps/docs check:content -- --fix` mechanically fixes the
`updated:` date and manifest ordering — nothing else.

These three checks run in CI (`.github/workflows/ci.yml`, "Documentation
content and prose checks" and "Documentation link and FAQ drift checks"). Run
them locally before you commit; a gate you did not execute did not pass.

## Voice rules

Prose lint (`scripts/lint-prose.ts`) fails the build on:

- **Exclamation marks** in prose.
- **The words `simply`, `just`, `obviously`, `easy`, `easily`.** If it were
simple the page would not exist.
- **Lower-cased `soroban`, `stellar`, `freighter`, `turborepo`** — capitalise
them. Contract names are `OrderVault`, `ExchangeRouter`, `SyntheticsReader`,
`DataStore`; never write them hyphenated in prose.

It warns (does not fail) on passive voice and sentences over 30 words. Treat the
warnings as review comments from the linter.

The rest of the voice guide — plain, specific, unhurried; concrete nouns over
abstract ones; hedge only when the uncertainty is real and then name it — is in
[`PROSE_STYLE.md`](./PROSE_STYLE.md).

## Previewing locally

```bash
bun install
bun run --cwd apps/docs dev
```

This builds the content and starts the Nitro dev server. Open the printed URL,
find your page in the sidebar, and check it in both light and dark themes at a
mobile and a desktop width.

## Sourcing claims

Every statement about protocol mechanics must be traceable to contract code,
the indexer, or `apps/web` behaviour — and the PR description must name the
file. "The faucet rejects a repeat claim with contract error code 6
(`ClaimTooSoon`), surfaced as a cooldown message"
(`apps/web/src/features/faucet/hooks/useClaim.tsx`) is sourced. "The faucet has
a cooldown" is not.

If the code does not handle a case, the page says so. A page that describes
intended behaviour as if it were current behaviour is worse than no page.

## Opening the PR

Use [`.github/PULL_REQUEST_TEMPLATE/documentation.md`](../../.github/PULL_REQUEST_TEMPLATE/documentation.md).
Append `?template=documentation.md` to the compare URL, or pick it from the
template dropdown. It mirrors the content map's definition of done (§4): the
page exists at its mapped path, the three checks pass, every mechanical claim
names its source file, the worked example's numbers were computed, and the page
is listed in `meta.json`.
60 changes: 60 additions & 0 deletions apps/docs/REVIEWER_CHECKLIST.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Documentation reviewer checklist

For reviewing content pull requests under `apps/docs/content/`. This checklist
is about **accuracy and reader fit**, not style — prose style is enforced by
`lint:prose` and refined in DX-053, and you should not spend review time on it.

The authoring rules are in [`CONTRIBUTING.md`](./CONTRIBUTING.md); the
information architecture and definition of done are in
[`docs/dx_1/003_content_map.md`](../../docs/dx_1/003_content_map.md).

---

## Accuracy

- [ ] **Every mechanical claim has a source.** The PR's Sources table names a
file for each statement about how the protocol behaves. Open two or three
of them and confirm the page matches the code, not a README summary.
- [ ] **The worked example is arithmetic, not a vibe.** The inputs, the steps,
and the result are all shown, and the result follows from the inputs.
- [ ] **Failure modes describe real behaviour.** Where the code does not handle
a case, the page says so plainly rather than describing the intended
handling as if it shipped.
- [ ] **No rotting numbers.** Fees, contract addresses, and schema are not
pasted inline — they are linked to the generated reference pages, or
stated as "current as of <date>".
- [ ] **Links point somewhere true.** `check:links` proves the routes resolve;
you confirm the linked page actually answers the question the link
implies.

## Reader fit

- [ ] **One reader per page.** It is obvious from the first paragraph whether
this is for a trader, a liquidity provider, or an integrator. A page
serving all three serves none.
- [ ] **The first paragraph answers the title.** A reader who stops after it
has a correct, if shallow, model.
- [ ] **Reading level matches the reader.** A concepts page for traders does
not assume Soroban knowledge; a developers page may.
- [ ] **Adjacent concepts are linked where the reader needs them**, not dumped
in a trailing list.

## Mechanics

- [ ] `check:content`, `check:links`, and `lint:prose` are green in CI, with no
new prose-lint warnings.
- [ ] The page is in `content/meta.json` in content-map §2 order (or is
`/index`, which is intentionally excluded).
- [ ] The `updated:` date is the date the work was actually finished.
- [ ] `status:` is `draft` if any claim in the PR's "Anything unverified"
section is still open.
- [ ] For guides: the PR confirms the author performed each documented action
on testnet.
- [ ] A `.changelog/unreleased/` entry with `area: docs` is included.

## Diagrams

- [ ] Renders in both themes.
- [ ] Has a caption that states what the diagram shows.
- [ ] Any image has non-empty alt text (`check:content` enforces this, but
confirm the alt text is descriptive, not a filename).
Loading
Loading