From 88625297a19402ce1d0715ca3199ecda6023bf00 Mon Sep 17 00:00:00 2001 From: August Date: Wed, 16 Sep 2026 00:33:59 +0000 Subject: [PATCH 1/4] docs(rules): add mdx-capability-guides skeleton for multi-surface how-tos --- .claude/rules/mdx-capability-guides.md | 85 ++++++++++++++++++++++++++ .claude/rules/mdx-components.md | 8 +-- .claude/rules/mdx-diagrams.md | 5 +- .claude/rules/mdx-style.md | 8 +-- 4 files changed, 97 insertions(+), 9 deletions(-) create mode 100644 .claude/rules/mdx-capability-guides.md diff --git a/.claude/rules/mdx-capability-guides.md b/.claude/rules/mdx-capability-guides.md new file mode 100644 index 0000000000..203169f817 --- /dev/null +++ b/.claude/rules/mdx-capability-guides.md @@ -0,0 +1,85 @@ +--- +paths: + - "fern/products/*/pages/**/*.mdx" +--- + +# Capability guides — one capability, every surface + +Applies to a how-to that teaches one platform capability across more than one surface (SWML, the REST Calling API, WebSocket via Relay, the Browser SDK). Exemplar: `fern/products/platform/pages/calling/voice/outbound-calling.mdx`; `call-streaming.mdx` and `machine-detection.mdx` beside it follow the same shape. A single-surface how-to or an integration guide (the Vapi page) follows `mdx-style` alone. Voice, grounding, and `## Next steps` come from `mdx-style`; this rule fixes the skeleton and the sample conventions so guides match each other. + +## Skeleton + +```text +frontmatter: title = capability as a noun phrase ("Call streaming"); slug /voice/; + description = one sentence naming the first-run outcome, then each extension in page order +[reference-style link definitions] +intro, 2–3 sentences, no heading: what the reader does, then "Start by , then ." +## Prepare for +## How works (optional — see below) +## ("Make your first call", "Stream your first call") + + ### Choose how to + ### Set your credentials and + ### (optional — "Choose a destination", "Run a WebSocket server") + ### ("Place the call", "Start the stream") + ### ("Answer the call", "Answer and speak") — the verification step + +## Track / Control + ### … via REST + ### … via WebSocket + ### Compare the surfaces (optional) +## Examples + ### + #### via REST + #### via WebSocket (Relay) +## Next steps +``` + +- **Prepare.** "Have these values ready:" then bullets: Space URL, Project ID and API token with the **Voice** permission, a purchased number or verified caller ID, a destination the reader can answer, then topic-specific items. Hard constraints follow as titled ``s (trial and international limits, `wss://` only, TCPA). Add one pointer sentence when a reader might want a sibling guide instead. +- **How it works.** Put it before the first run only when the reader can't interpret the result without the model (machine detection's outcome table). Otherwise conceptual depth goes after the first run, as its own `##` ("Handle the audio stream"). +- **First run** targets the smallest thing the reader can confirm alone, usually calling their own phone. +- **Verification step** closes the ``: what the reader hears or sees on success, then the likeliest failure and what it means. Never skip it. +- **Track/Control** opens with "Follow the section for the approach you used." REST gets `status_url`/`status_events` and a JSON callback payload; WebSocket gets `call.on(…)` handlers. One lifecycle diagram per surface family, themed SVG in `` with an `` mermaid `sequenceDiagram` twin (`mdx-diagrams`). Add `### Compare the surfaces` when defaults or parameter names differ by surface. +- **Examples** are `### ` scenarios: one-sentence goal, a titled `` when compliance applies, then a `####` per surface. Every example is a complete program the reader can run, not a fragment. +- **Next steps** is required here as on every guide (`mdx-style`). Cards deepen this capability only. + +## Choose-how tables + +Two tables, in this order: + +1. `| What you want to do | Where to start |` — one row per surface; the second cell links to the do-it step's anchor and names the surface ("[REST Calling API](#place-the-call), using cURL or a Server SDK"). +2. `| Function | SWML | REST | WebSocket (Relay) | Browser SDK |` (only the surfaces the capability has) — cells are `` or ``. Rows are reader outcomes ("Command a call already in progress from any process, by its call ID"), not feature names. + +Close with one sentence on what the approaches share and where they differ. Surface names are fixed: "SWML", "REST Calling API" (or "REST"), "WebSocket (Relay)", "Browser SDK", "Server SDK". Never "Realtime SDK" or "Agents SDK". + +## Surface tabs and code blocks + +- The do-it step is `` with exactly `` and ``. Guides under one overview share a single `groupId` so the reader's surface choice follows them from page to page; the voice guides use `outbound-api`. Each tab opens with one paragraph on what the request does, then ``. +- CodeBlock titles are ``: REST tab `cURL — Calling API`, `Python — REST client`, `TypeScript — REST client`; Relay tab `Python — Relay client`, `TypeScript — Relay client`, plus `JavaScript — Browser SDK` when the browser applies. +- The REST tab ends with the "returns a call `id` and status `queued` … save the `id`" paragraph and ``. +- A SWML-only sample is `` of `YAML` and `JSON`. Later `####`s outside the tabs use bare ``, not nested tabs. + +## Sample conventions + +- **Header comments** on every standalone sample. Python: `# Install: python -m pip install signalwire-sdk==3.4.1` then `# Save as .py and run: python .py`. TypeScript with no type annotations: `// Install: npm install @signalwire/sdk@2.0.5` then `// This sample also runs as JavaScript: save as .mjs,` / `// then run: node .mjs`. TypeScript that uses types: `// Save as .mts and run: npx tsx .mts`. Browser: `// Install: npm install @signalwire/js@4.0.0-rc.2 rxjs@7.8.2` plus the HTML elements the script expects, as comments. +- Every sample under `## Examples` is standalone with headers. A block under `## Track…`/`## Control…` may show only the changed lines when its paragraph names the sample it extends. +- **Pinned versions** are literal and identical across every guide: `signalwire-sdk==3.4.1`, `@signalwire/sdk@2.0.5`, `@signalwire/js@4.0.0-rc.2 rxjs@7.8.2`. Bump them in every guide in one PR, never one page at a time. +- **Placeholders**: ``, ``, ``, ``, ``, ``, ``; new ones follow the same `` shape and appear in the credentials table. +- **Relay boilerplate**: `contexts=["default"]`, `timeout: 30` on the phone device, Python `async with client:` and `await call.wait_for_ended()`, TypeScript `await client.connect()` / `try … finally { await client.disconnect(); }` and `await call.waitForEnded()`. Hang up in an `on_completed`/`onCompleted` callback guarded by `call.state !== "ended"`. +- **Builder gaps**: when the SDK's SWML builder lacks a verb, say so in a comment and use `add_verb`/`addVerb` with validation off for that verb only. Don't silently hand-write JSON where the builder works. +- **Running example**: `Bayview Taxi` and its dispatcher `Ada` when the scenario has a business voice; a plain "Hello, welcome to SignalWire!" for pure mechanics (`mdx-style` Grounding). + +## Ships with the page + +- A `` on the parent overview (`fern/products/platform/pages/calling/voice/overview.mdx` for voice), and a fix for any FAQ or stale link there that the guide now answers. +- The `fern/llms.txt` bullet: add or repoint, description copied from the page's frontmatter. +- Redirects in `fern/docs.yml` only when the guide consolidates existing pages. +- No changelog entry (documentation work) and no nav yml change (folder-driven nav). + +## Before opening the PR + +Extract every fenced block and validate it against the pinned SDKs: JSON and YAML parse; bash syntax (`bash -n`); Python `py_compile` and `pyflakes`; TypeScript `tsc --strict` against the installed `@signalwire/sdk`; every SDK method, parameter, and event field checked against the installed package, not only the reference pages. Run each SWML builder chain and diff its output against the hand-written JSON sample beside it. Confirm every internal link against the target's `slug:` and every `#anchor` by hand. Then `yarn fern-md-check`. + +PR body sections: `## Summary` (bullets), `## Notes for reviewers` (cross-PR link dependencies, upstream SDK or doc inconsistencies worked around), `## Verification` (the checks above, stated as run), and the preview URLs of every page changed. + +*Source: PRs #660, #692, #693 (2026-09), which established the shape. [SW]* diff --git a/.claude/rules/mdx-components.md b/.claude/rules/mdx-components.md index 9b17e3c8e2..e590e2c018 100644 --- a/.claude/rules/mdx-components.md +++ b/.claude/rules/mdx-components.md @@ -16,15 +16,15 @@ Every component available on this site: Fern's full default library (description | `Button` | Interactive button with styles, sizes, intents, icons | Unused | | `Callout` → named variants `Note` `Info` `Warning` `Tip` `Check` `Error` `Success` `Launch` | Highlighted admonition block with intent and optional custom icon | House set: `Note`/`Tip`/`Warning`/`Info` = prereq / suggestion / hazard / pointer; `Error` rare (×2). Write the named variant, never generic `` | | `Card` / `CardGroup` | Boxed content and link grids | In use — overviews and `## Next steps`. `` is an undocumented legacy alias (×2 old pages); use `CardGroup` | -| Code blocks: fenced + `CodeBlocks` / `CodeBlock` | Syntax highlighting with `title="…"`, line highlights `{6,9}`, focus | House: single example = plain fence; several languages/formats = `CodeBlocks` of titled `CodeBlock`s. `CodeGroup` (Fern's `for=`-synced grouping) unused here | +| Code blocks: fenced + `CodeBlocks` / `CodeBlock` | Syntax highlighting with `title="…"`, line highlights `{6,9}`, focus | House: single example = plain fence; several languages/formats = `CodeBlocks` of titled `CodeBlock`s, titled `` (`Python — REST client`) in guides. `CodeGroup` (Fern's `for=`-synced grouping) unused here | | `Copy` | Click-to-copy for arbitrary text | Unused | | `Download` | Lets readers download PDFs, files, ZIP bundles | Unused | | `EndpointRequestSnippet` | Embeds an endpoint's request example from the API Reference | In use — pair with a link to the reference page | -| `EndpointResponseSnippet` | Embeds an endpoint's response example | In use | +| `EndpointResponseSnippet` | Embeds an endpoint's response example | In use — closes the REST tab of a capability guide's first-run step | | `EndpointSchemaSnippet` | Embeds an endpoint's schema | In use — heavily, in SDK reference | | `Files` | Interactive file tree with expandable folders | Unused | | `Frame` | Image or diagram with caption and background variants | In use — wrap meaningful images (themed SVGs excepted — see `mdx-diagrams`) | -| `Icon` | Inline Font Awesome icon with size/color/style | Rare (×2) — short-form icon syntax per `mdx-mechanics` | +| `Icon` | Inline Font Awesome icon with size/color/style | In use — capability matrices in guides use `regular circle-check` / `regular circle-xmark` with `color="var(--status-success)"` / `color="var(--status-error)"` (`mdx-capability-guides`); otherwise rare. Short-form icon syntax per `mdx-mechanics` | | `If` | Shows/hides content by product, version, or reader role | Unused | | `Indent` | Left indentation for nested parameters and hierarchy | In use — nests `ParamField`s (blank lines inside) | | `Markdown src="…"` | Includes a reusable snippet from `fern/snippets/`; supports `{{param}}` substitution | In use — self-closing; only genuinely shared content | @@ -35,7 +35,7 @@ Every component available on this site: Fern's full default library (description | `SchemaSnippet` | Displays a type definition as a JSON code block | Unused | | `Steps` / `Step` | Auto-numbered sequential walkthrough | In use — ordered procedures in guides | | Tables (markdown) | Rows and columns, optional sticky header | Native markdown — genuine enumerations only (see `mdx-style`) | -| `Tabs` / `Tab` | Tabbed views with language synchronization | In use — switchable non-code variants | +| `Tabs` / `Tab` | Tabbed views with language synchronization | In use — switchable non-code variants; surface switching in guides (`REST` / `WebSocket (Relay)`) with a `groupId` so the reader's choice persists down the page | | `Tooltip` | Contextual info on hover, for text and code | Rare (×6) — only where it genuinely helps | | `Versions` | Content that switches with a version dropdown | Unused | | `WebhookPayloadSnippet` | Embeds a webhook payload example from the API Reference | In use | diff --git a/.claude/rules/mdx-diagrams.md b/.claude/rules/mdx-diagrams.md index 63476c5f5f..22774e7565 100644 --- a/.claude/rules/mdx-diagrams.md +++ b/.claude/rules/mdx-diagrams.md @@ -9,7 +9,10 @@ paths: ## Themed SVG diagrams Applies to hand-authored SVG diagrams under `fern/assets/images/img/`. -Exemplar: `ai-agent-flow-themed.svg`. +Exemplar: `ai-agent-flow-themed.svg`. Sequence-diagram exemplars from the +capability guides (pending #660, #692, #693): `outbound-call-lifecycle-themed.svg`, +`outbound-call-relay-lifecycle-themed.svg`, `call-stream-lifecycle-themed.svg`, +`machine-detection-flow-themed.svg`. ### One file, both modes diff --git a/.claude/rules/mdx-style.md b/.claude/rules/mdx-style.md index ce452e112a..e38055529d 100644 --- a/.claude/rules/mdx-style.md +++ b/.claude/rules/mdx-style.md @@ -6,14 +6,14 @@ paths: # Writing style — SignalWire docs -How pages should read. Mechanics: `mdx-mechanics`; components: `mdx-components`; ParamField/reference patterns: `mdx-reference-pages`; what's generated vs editable: `docs-conventions`. +How pages should read. Mechanics: `mdx-mechanics`; components: `mdx-components`; ParamField/reference patterns: `mdx-reference-pages`; multi-surface how-to skeleton: `mdx-capability-guides`; what's generated vs editable: `docs-conventions`. ## Page types (Diátaxis) [D] Pick by asking: is the reader **learning or working**, and do they need **action or understanding**? - **Tutorial** (learning + action; get-started paths): a lesson that guarantees a working result. One path, concrete steps, visible progress early. Voice: encouraging and steady — build the reader's confidence. Keep out: options, digressions, theory. -- **How-to guide** (working + action): starts from a goal, assumes setup. Voice: direct. Verb-first title, conditions before instructions, only meaningful choices. Keep out: re-teaching basics (link them), exhaustive edge cases. +- **How-to guide** (working + action): starts from a goal, assumes setup. Voice: direct. Verb-first title, conditions before instructions, only meaningful choices. Keep out: re-teaching basics (link them), exhaustive edge cases. A how-to that teaches one capability across several surfaces follows the `mdx-capability-guides` skeleton. - **Reference** (working + lookup): austere, neutral, factual, complete, structured like the product. Keep out: persuasion, instruction beyond usage notes. - **Explanation** (learning + understanding): why it works this way. Voice: discursive and reflective — the one place tradeoffs, background, and opinions belong. Keep out: step-by-step procedures. @@ -49,12 +49,12 @@ Tutorials and how-tos give the reader a way to confirm the result — a command - Customer-facing only: no internal implementation detail (backend endpoints, transports, engine-side params). Placeholder data only — no real names, emails, or domains. - Examples do real work: tool call → your server → system of record → response. No knowledge-trivia bots. - Examples run as written: real imports, every required parameter, and only placeholders the reader can obviously fill. Verify each against the spec or SDK source — and against an actual run when the page ships a whole flow. -- A cluster of guides shares one running example, so cross-links compound instead of restarting context. The AI guides use Bayview Taxi and its dispatcher Ada across `platform/pages/ai/overview.mdx` and `platform/pages/ai/guides/**`; adopt a cluster's existing cast when you add a page to it, and introduce a new one only for a new cluster. [SW] +- A cluster of guides shares one running example, so cross-links compound instead of restarting context. The AI guides use Bayview Taxi and its dispatcher Ada across `platform/pages/ai/overview.mdx` and `platform/pages/ai/guides/**`, and the calling guides under `platform/pages/calling/voice/` borrow the same cast whenever a scenario has a business voice; adopt a cluster's existing cast when you add a page to it, and introduce a new one only for a new cluster. [SW] - Don't cite other vendors' docs as authority; never disparage anyone's product. ## Modes -- **New page**: pick the type above, then mirror the best sibling page of that type; frontmatter per `mdx-mechanics`. +- **New page**: pick the type above. A multi-surface how-to follows the `mdx-capability-guides` skeleton and its exemplar; anything else mirrors the best sibling page of that type. Frontmatter per `mdx-mechanics`. - **Editing**: everything you write or rewrite follows these rules; leave untouched text alone and flag its violations in your summary instead. Preserve meaning, structure, and length unless asked. "Polish" means structural first — mental model, complete example — then sentences. - Legacy-page tells (strip from parts you rewrite; don't sweep the rest without an explicit cleanup task): `id:`/`x-custom`/`ported_from_readme` frontmatter, "In this guide…" intros, editorial "we", Title-Case headings. [SW] From 4f1003353c536f7bb6d3382241c847b23471b85a Mon Sep 17 00:00:00 2001 From: August Date: Wed, 16 Sep 2026 00:34:30 +0000 Subject: [PATCH 2/4] docs(rules): cross-reference capability guides from style, components, and diagrams rules --- .claude/docs/mdx-style-reference.md | 13 +++++++++++++ CLAUDE.md | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/.claude/docs/mdx-style-reference.md b/.claude/docs/mdx-style-reference.md index 3a18cfd826..b74bd4343a 100644 --- a/.claude/docs/mdx-style-reference.md +++ b/.claude/docs/mdx-style-reference.md @@ -23,6 +23,19 @@ Anti-pattern smells (names from developer-docs-framework, kept for their diagnos - **Dead End** — a page with no onward links. Guides close with topical Next steps. - **Feature Mirror** — guides organized by product feature instead of user goal. +## Capability guide template — rationale + +`mdx-capability-guides` fixes the skeleton for a how-to that teaches one capability across several surfaces. The shape came from three pages written in September 2026 (PRs #660 outbound calling, #692 call streaming, #693 machine detection); the second and third copied the first deliberately, and the rule writes down what they share so the fourth matches without reverse-engineering an 1,800-line page. Why each part is the way it is: + +- **First run → track → examples.** Diátaxis tutorials want visible progress early; how-tos want the goal met. The template borrows the tutorial's opening (call your own phone within the first screen) and then widens into how-to depth, so a reader who only needs the basic call leaves after `` and a reader building a product keeps going. Concept sections sit after the first run unless the reader can't read the result without them. +- **Surfaces as tabs on one page, not one page per surface.** The capability has one URL, so cross-links and `llms.txt` point at one place, and `groupId` keeps the reader's REST-or-Relay choice as they scroll. Separate pages per surface produced the Feature Mirror smell and drifted apart. +- **Two choose-how tables.** The first answers "which surface do I want" by outcome; the second is the honest capability matrix, rows phrased as things the reader wants to do. Together they replace the paragraph of hedged prose that older guides used to compare approaches. +- **Prepare as a value list.** Readers skim it for what to copy; prose hid the same facts. Constraints are titled ``s because the Markdown export flattens untitled callouts. +- **Every example is a complete program.** Fragments forced readers to reassemble state from three places up the page. The header comments (`Install:` / `Save as … and run:`) make each block executable as pasted and are the mechanism by which the code-validation procedure works. +- **Literal pinned versions.** The TypeScript SDK's REST `dial` signature changed between the published 2.0.5 and the unreleased next major; guides pinned to a literal version stay true until someone bumps every guide at once. Unpinned installs would silently break the samples. +- **`## Next steps` stays.** The three founding pages ended on their last example with inline onward links. The house rule kept the closing card grid, both for the Dead End smell and because the export and the sidebar treat it as the page's summary of where to go next; the founding pages get it on review. +- **Settled drifts.** Header comments: every `## Examples` sample is standalone; `## Track…` blocks may show only changed lines when the paragraph names the sample they extend. Concept placement: before the first run only when needed to interpret it. Both are defaults, not doctrine; change them in the rule, not per page. + ## House voice profile (corpus evidence, 2,801 pages) The corpus is two populations, not two opinions: modern-authored pages already match the target voice; nearly all drift sits on legacy ReadMe-ported pages (markers: `id:` UUID ×412 pages, `x-custom`/`ported_from_readme` ×55). The voice rules mostly *defend won ground* — classic AI tells are near-zero on modern pages (Furthermore ×0, Moreover ×1, In conclusion ×0). diff --git a/CLAUDE.md b/CLAUDE.md index 8d947d3f24..5d678b7ae3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ Fern-powered documentation site. Two sources of truth: MDX pages under `fern/pro ## Authoring rules -`.claude/rules/` holds the binding conventions. They are path-scoped and fire when a matching file is read — so when you create a file from scratch, open the matching rule first: `mdx-style`, `mdx-mechanics`, `mdx-components`, `mdx-reference-pages`, `docs-conventions` for anything under `fern/`; `spec-conventions` for `specs/`. Rationale and depth behind the style rules: `.claude/docs/mdx-style-reference.md` (read on demand, never auto-loads). +`.claude/rules/` holds the binding conventions. They are path-scoped and fire when a matching file is read — so when you create a file from scratch, open the matching rule first: `mdx-style`, `mdx-mechanics`, `mdx-components`, `mdx-reference-pages`, `docs-conventions` for anything under `fern/`; `mdx-capability-guides` for a how-to that covers one capability across several surfaces; `spec-conventions` for `specs/`. Rationale and depth behind the style rules: `.claude/docs/mdx-style-reference.md` (read on demand, never auto-loads). ## Root llms.txt is hand-maintained From fce7c5a8f255b684af344d5b4a226bdbbe6ee504 Mon Sep 17 00:00:00 2001 From: August Date: Wed, 16 Sep 2026 14:29:14 +0000 Subject: [PATCH 3/4] docs(rules): remove rationale section --- .claude/docs/mdx-style-reference.md | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/.claude/docs/mdx-style-reference.md b/.claude/docs/mdx-style-reference.md index b74bd4343a..3a18cfd826 100644 --- a/.claude/docs/mdx-style-reference.md +++ b/.claude/docs/mdx-style-reference.md @@ -23,19 +23,6 @@ Anti-pattern smells (names from developer-docs-framework, kept for their diagnos - **Dead End** — a page with no onward links. Guides close with topical Next steps. - **Feature Mirror** — guides organized by product feature instead of user goal. -## Capability guide template — rationale - -`mdx-capability-guides` fixes the skeleton for a how-to that teaches one capability across several surfaces. The shape came from three pages written in September 2026 (PRs #660 outbound calling, #692 call streaming, #693 machine detection); the second and third copied the first deliberately, and the rule writes down what they share so the fourth matches without reverse-engineering an 1,800-line page. Why each part is the way it is: - -- **First run → track → examples.** Diátaxis tutorials want visible progress early; how-tos want the goal met. The template borrows the tutorial's opening (call your own phone within the first screen) and then widens into how-to depth, so a reader who only needs the basic call leaves after `` and a reader building a product keeps going. Concept sections sit after the first run unless the reader can't read the result without them. -- **Surfaces as tabs on one page, not one page per surface.** The capability has one URL, so cross-links and `llms.txt` point at one place, and `groupId` keeps the reader's REST-or-Relay choice as they scroll. Separate pages per surface produced the Feature Mirror smell and drifted apart. -- **Two choose-how tables.** The first answers "which surface do I want" by outcome; the second is the honest capability matrix, rows phrased as things the reader wants to do. Together they replace the paragraph of hedged prose that older guides used to compare approaches. -- **Prepare as a value list.** Readers skim it for what to copy; prose hid the same facts. Constraints are titled ``s because the Markdown export flattens untitled callouts. -- **Every example is a complete program.** Fragments forced readers to reassemble state from three places up the page. The header comments (`Install:` / `Save as … and run:`) make each block executable as pasted and are the mechanism by which the code-validation procedure works. -- **Literal pinned versions.** The TypeScript SDK's REST `dial` signature changed between the published 2.0.5 and the unreleased next major; guides pinned to a literal version stay true until someone bumps every guide at once. Unpinned installs would silently break the samples. -- **`## Next steps` stays.** The three founding pages ended on their last example with inline onward links. The house rule kept the closing card grid, both for the Dead End smell and because the export and the sidebar treat it as the page's summary of where to go next; the founding pages get it on review. -- **Settled drifts.** Header comments: every `## Examples` sample is standalone; `## Track…` blocks may show only changed lines when the paragraph names the sample they extend. Concept placement: before the first run only when needed to interpret it. Both are defaults, not doctrine; change them in the rule, not per page. - ## House voice profile (corpus evidence, 2,801 pages) The corpus is two populations, not two opinions: modern-authored pages already match the target voice; nearly all drift sits on legacy ReadMe-ported pages (markers: `id:` UUID ×412 pages, `x-custom`/`ported_from_readme` ×55). The voice rules mostly *defend won ground* — classic AI tells are near-zero on modern pages (Furthermore ×0, Moreover ×1, In conclusion ×0). From 6da9c03dbb9cedc07f4d145f0f1196fcbc6bd363 Mon Sep 17 00:00:00 2001 From: August Date: Wed, 16 Sep 2026 14:32:12 +0000 Subject: [PATCH 4/4] docs(rules): apply guide template rulings to capability guides --- .claude/rules/mdx-capability-guides.md | 77 +++++++++++++++----------- .claude/rules/mdx-components.md | 4 +- .claude/rules/mdx-diagrams.md | 6 +- 3 files changed, 48 insertions(+), 39 deletions(-) diff --git a/.claude/rules/mdx-capability-guides.md b/.claude/rules/mdx-capability-guides.md index 203169f817..fa28bd8014 100644 --- a/.claude/rules/mdx-capability-guides.md +++ b/.claude/rules/mdx-capability-guides.md @@ -5,66 +5,77 @@ paths: # Capability guides — one capability, every surface -Applies to a how-to that teaches one platform capability across more than one surface (SWML, the REST Calling API, WebSocket via Relay, the Browser SDK). Exemplar: `fern/products/platform/pages/calling/voice/outbound-calling.mdx`; `call-streaming.mdx` and `machine-detection.mdx` beside it follow the same shape. A single-surface how-to or an integration guide (the Vapi page) follows `mdx-style` alone. Voice, grounding, and `## Next steps` come from `mdx-style`; this rule fixes the skeleton and the sample conventions so guides match each other. +Applies to a how-to that teaches one platform capability across more than one surface (SWML, Relay, the REST Calling API, the Browser SDK, Call Flow Builder). Exemplars: `fern/products/platform/pages/calling/voice/outbound-calling.mdx` (first-run walkthrough, complete samples) and `fern/products/platform/pages/calling/record-calls.mdx` (task sections, merged Relay and REST blocks). A single-surface how-to or an integration guide (the Vapi page) follows `mdx-style` alone. Voice, grounding, and `## Next steps` come from `mdx-style`; this rule fixes the skeleton and the sample conventions so guides match each other. ## Skeleton +Every guide answers the same questions in the same order: which product, how to start, how to do the thing, how to know it worked, where to go next. + ```text -frontmatter: title = capability as a noun phrase ("Call streaming"); slug /voice/; +frontmatter: title = capability as a noun phrase ("Call streaming"); slug //; description = one sentence naming the first-run outcome, then each extension in page order [reference-style link definitions] intro, 2–3 sentences, no heading: what the reader does, then "Start by , then ." +## Pick the right product for capability matrix + one bullet per surface ## Prepare for -## How works (optional — see below) -## ("Make your first call", "Stream your first call") +## How works (optional — see below) +## (only for a reader starting from zero — see below) - ### Choose how to ### Set your credentials and - ### (optional — "Choose a destination", "Run a WebSocket server") - ### ("Place the call", "Start the stream") - ### ("Answer the call", "Answer and speak") — the verification step + ### (optional — "Choose a destination", "Run a WebSocket server") + ### ("Place the call", "Start the stream") + ### ("Answer the call", "Answer and speak") — the verification step -## Track / Control - ### … via REST - ### … via WebSocket - ### Compare the surfaces (optional) +## one per task the guide must cover + ### via one per surface that supports it ## Examples ### - #### via REST - #### via WebSocket (Relay) + #### via ## Next steps ``` +- **Pick the right product.** The first job of the page is to show which surface can do what, so the reader chooses before reading further. See "Capability matrix" below. - **Prepare.** "Have these values ready:" then bullets: Space URL, Project ID and API token with the **Voice** permission, a purchased number or verified caller ID, a destination the reader can answer, then topic-specific items. Hard constraints follow as titled ``s (trial and international limits, `wss://` only, TCPA). Add one pointer sentence when a reader might want a sibling guide instead. - **How it works.** Put it before the first run only when the reader can't interpret the result without the model (machine detection's outcome table). Otherwise conceptual depth goes after the first run, as its own `##` ("Handle the audio stream"). -- **First run** targets the smallest thing the reader can confirm alone, usually calling their own phone. -- **Verification step** closes the ``: what the reader hears or sees on success, then the likeliest failure and what it means. Never skip it. -- **Track/Control** opens with "Follow the section for the approach you used." REST gets `status_url`/`status_events` and a JSON callback payload; WebSocket gets `call.on(…)` handlers. One lifecycle diagram per surface family, themed SVG in `` with an `` mermaid `sequenceDiagram` twin (`mdx-diagrams`). Add `### Compare the surfaces` when defaults or parameter names differ by surface. -- **Examples** are `### ` scenarios: one-sentence goal, a titled `` when compliance applies, then a `####` per surface. Every example is a complete program the reader can run, not a fragment. +- **First run.** A `` walkthrough is for a reader starting from zero: a first call, a first stream. A guide that picks up mid-stream (recording assumes you can already place a call) skips `` and opens with its first task section. The walkthrough targets the smallest thing the reader can confirm alone, usually calling their own phone: one code block per surface, no options, no error handling. +- **Verification step** closes the ``: what the reader hears or sees on success, then the likeliest failure and what it means. A guide without `` puts the same check at the end of its first task section. Never skip it. +- **Task sections** are the body of the guide. Name each `##` after what the reader wants ("Record the whole call"), never after the API surface that provides it ("The record_call verb"). One `###` per surface beneath it, surface named in the heading. Options get their own `##` after the task that introduces them ("Recording options: format, stereo, and direction"). +- **Track and control** is a task section like any other: REST gets `status_url`/`status_events` and a JSON callback payload; Relay gets `call.on(…)` handlers. One lifecycle diagram per surface family, themed SVG in `` with an `` mermaid `sequenceDiagram` twin (`mdx-diagrams`). Add `### Compare the surfaces` when defaults or parameter names differ by surface. +- **Examples** hold the variations you could delete and still have a guide. If removing a section would leave a hole in the main path, it is a task section and belongs above `## Examples`. Each example is `### `: one-sentence goal, a titled `` when compliance applies, then a `####` per surface. - **Next steps** is required here as on every guide (`mdx-style`). Cards deepen this capability only. -## Choose-how tables +## Capability matrix + +One table under `## Pick the right product`: `| Function | SWML | Relay | REST Calling API | Browser SDK | Call Flow Builder |`, keeping every column where the capability plausibly lives and adding an API-area column (`Recordings API`) when a REST resource is part of the story. Rows are things the reader wants to do ("Get the recording's URL back in your own code, without a webhook"), not feature names. Three cell states: + +- `` shipped +- `` not available +- `` planned, not shipped — add the legend line under the table only when a cell uses it + +Follow the table with one bullet per column: the surface's canonical name linked to its reference, and one sentence on what it is for. Agents is never its own column: it generates SWML, so it can express anything the SWML column can. -Two tables, in this order: +## Surface names -1. `| What you want to do | Where to start |` — one row per surface; the second cell links to the do-it step's anchor and names the surface ("[REST Calling API](#place-the-call), using cURL or a Server SDK"). -2. `| Function | SWML | REST | WebSocket (Relay) | Browser SDK |` (only the surfaces the capability has) — cells are `` or ``. Rows are reader outcomes ("Command a call already in progress from any process, by its call ID"), not feature names. +Canonical names come from the glossary (`/docs/platform/glossary`); the ones this rule uses are `SWML`, `Relay`, `REST Calling API`, `Server SDKs`, `Browser SDK`, `Call Flow Builder`. Never "Realtime SDK", "Agents SDK", "the WebSocket API", or "CFB". How specific to be depends on where the name sits: -Close with one sentence on what the approaches share and where they differ. Surface names are fixed: "SWML", "REST Calling API" (or "REST"), "WebSocket (Relay)", "Browser SDK", "Server SDK". Never "Realtime SDK" or "Agents SDK". +- **Headings** name the surface in full: `### Stop recording the call via Relay`, `### Place the call via the REST Calling API`. Two shapes: ` via ` when the surface is a transport; ` from ` when it is a caller context ("Hold an AI chat conversation from a browser"). Every heading stands alone in a search result with no page title above it, so it carries the keywords: "Recording options: format, stereo, and direction", not "Recording options". +- **Tab titles** are short: `REST`, `WebSocket (Relay)`. +- **Code block titles** are ``: `Python — Relay client`, `TypeScript — Relay client`, `cURL — REST Calling API`, `Python — Agents`, `TypeScript — Agents`, `Python — REST client`, `TypeScript — REST client`, `JavaScript — Browser SDK`, `YAML`, `JSON`. -## Surface tabs and code blocks +## Surfaces: headings by default, tabs only inside Steps -- The do-it step is `` with exactly `` and ``. Guides under one overview share a single `groupId` so the reader's surface choice follows them from page to page; the voice guides use `outbound-api`. Each tab opens with one paragraph on what the request does, then ``. -- CodeBlock titles are ``: REST tab `cURL — Calling API`, `Python — REST client`, `TypeScript — REST client`; Relay tab `Python — Relay client`, `TypeScript — Relay client`, plus `JavaScript — Browser SDK` when the browser applies. -- The REST tab ends with the "returns a call `id` and status `queued` … save the `id`" paragraph and ``. -- A SWML-only sample is `` of `YAML` and `JSON`. Later `####`s outside the tabs use bare ``, not nested tabs. +- A task shows each surface under its own `###`, never in tabs. Headings appear in the table of contents, in search, and in the Markdown export; tab panes hide from all three. +- Inside ``, `###` is already the step heading, so a step that differs by surface uses `` with `` and ``. That is the only place tabs belong. Guides under one overview share a single `groupId` so the reader's choice follows them between pages; the voice guides use `outbound-api`. +- **Relay and the REST Calling API are the same commands over two transports.** When only the transport differs (pausing a recording, stopping a stream), one `### via Relay` heading holds a single `` of `Python — Relay client`, `TypeScript — Relay client`, and `cURL — REST Calling API`. Split them into separate headings only when the flow genuinely differs: placing a call over HTTP with inline SWML is not the same flow as dialing over a live socket. +- **SWML is one surface, authored several ways and delivered several ways.** Under `### via SWML`, show the document once, in the forms a reader authors it, in one ``: `Python — Agents` and `TypeScript — Agents` (`AgentBase`) first, then `YAML`, then `JSON`. Don't encourage hand-writing SWML where the SDK covers the verb. How the document reaches the call (served from your server, stored as a hosted script resource, passed inline in a REST `dial`) is a delivery choice: cover it once per guide, in the first-run section, and don't repeat it under every task. A REST `dial` that carries inline SWML is SWML delivery, not the REST Calling API surface. +- Where the reference already documents a REST request, use `EndpointRequestSnippet` and `EndpointResponseSnippet` with a link to the reference page instead of hand-writing the request; describe only the fields the task needs. A first-run REST step that dials still ends with the "returns a call `id` and status `queued` … save the `id`" paragraph and ``. ## Sample conventions -- **Header comments** on every standalone sample. Python: `# Install: python -m pip install signalwire-sdk==3.4.1` then `# Save as .py and run: python .py`. TypeScript with no type annotations: `// Install: npm install @signalwire/sdk@2.0.5` then `// This sample also runs as JavaScript: save as .mjs,` / `// then run: node .mjs`. TypeScript that uses types: `// Save as .mts and run: npx tsx .mts`. Browser: `// Install: npm install @signalwire/js@4.0.0-rc.2 rxjs@7.8.2` plus the HTML elements the script expects, as comments. -- Every sample under `## Examples` is standalone with headers. A block under `## Track…`/`## Control…` may show only the changed lines when its paragraph names the sample it extends. +- **Complete programs.** Every sample under `` and `## Examples` runs as pasted: imports, client setup, the call, and the wait or disconnect. A block under a task section may show only the changed lines, with line highlights (`{6-8}`), when its paragraph names the sample it extends. +- **Header comments** on every complete sample. Python: `# Install: python -m pip install signalwire-sdk==3.4.1` then `# Save as .py and run: python .py`. TypeScript with no type annotations: `// Install: npm install @signalwire/sdk@2.0.5` then `// This sample also runs as JavaScript: save as .mjs,` / `// then run: node .mjs`. TypeScript that uses types: `// Save as .mts and run: npx tsx .mts`. Browser: `// Install: npm install @signalwire/js@4.0.0-rc.2 rxjs@7.8.2` plus the HTML elements the script expects, as comments. - **Pinned versions** are literal and identical across every guide: `signalwire-sdk==3.4.1`, `@signalwire/sdk@2.0.5`, `@signalwire/js@4.0.0-rc.2 rxjs@7.8.2`. Bump them in every guide in one PR, never one page at a time. -- **Placeholders**: ``, ``, ``, ``, ``, ``, ``; new ones follow the same `` shape and appear in the credentials table. +- **Placeholders**: ``, ``, ``, ``, ``, ``, ``; new ones follow the same `` shape. Explain them once, in a `| Value | Replace with |` table before the first code block. - **Relay boilerplate**: `contexts=["default"]`, `timeout: 30` on the phone device, Python `async with client:` and `await call.wait_for_ended()`, TypeScript `await client.connect()` / `try … finally { await client.disconnect(); }` and `await call.waitForEnded()`. Hang up in an `on_completed`/`onCompleted` callback guarded by `call.state !== "ended"`. - **Builder gaps**: when the SDK's SWML builder lacks a verb, say so in a comment and use `add_verb`/`addVerb` with validation off for that verb only. Don't silently hand-write JSON where the builder works. - **Running example**: `Bayview Taxi` and its dispatcher `Ada` when the scenario has a business voice; a plain "Hello, welcome to SignalWire!" for pure mechanics (`mdx-style` Grounding). @@ -74,7 +85,7 @@ Close with one sentence on what the approaches share and where they differ. Surf - A `` on the parent overview (`fern/products/platform/pages/calling/voice/overview.mdx` for voice), and a fix for any FAQ or stale link there that the guide now answers. - The `fern/llms.txt` bullet: add or repoint, description copied from the page's frontmatter. - Redirects in `fern/docs.yml` only when the guide consolidates existing pages. -- No changelog entry (documentation work) and no nav yml change (folder-driven nav). +- No changelog entry (documentation work). A nav yml entry only where the folder isn't folder-driven (`platform.yml` lists `pages/calling/` pages explicitly). ## Before opening the PR @@ -82,4 +93,4 @@ Extract every fenced block and validate it against the pinned SDKs: JSON and YAM PR body sections: `## Summary` (bullets), `## Notes for reviewers` (cross-PR link dependencies, upstream SDK or doc inconsistencies worked around), `## Verification` (the checks above, stated as run), and the preview URLs of every page changed. -*Source: PRs #660, #692, #693 (2026-09), which established the shape. [SW]* +*Sources: PRs #640 (write-guides template, record calls, AI chat), #660, #692, #693 (2026-09), reconciled 2026-09-16. [SW]* diff --git a/.claude/rules/mdx-components.md b/.claude/rules/mdx-components.md index 5af2c840c3..3079e5067d 100644 --- a/.claude/rules/mdx-components.md +++ b/.claude/rules/mdx-components.md @@ -20,7 +20,7 @@ Every component available on this site: Fern's full default library (description | `Copy` | Click-to-copy for arbitrary text | Unused | | `Download` | Lets readers download PDFs, files, ZIP bundles | Unused | | `EndpointRequestSnippet` | Embeds an endpoint's request example from the API Reference | In use — pair with a link to the reference page | -| `EndpointResponseSnippet` | Embeds an endpoint's response example | In use — closes the REST tab of a capability guide's first-run step | +| `EndpointResponseSnippet` | Embeds an endpoint's response example | In use — prefer it, with `EndpointRequestSnippet`, over a hand-written REST request the reference already documents | | `EndpointSchemaSnippet` | Embeds an endpoint's schema | In use — heavily, in SDK reference | | `Files` | Interactive file tree with expandable folders | Unused | | `Frame` | Image or diagram with caption and background variants | In use — wrap meaningful images (themed SVGs excepted — see `mdx-diagrams`) | @@ -35,7 +35,7 @@ Every component available on this site: Fern's full default library (description | `SchemaSnippet` | Displays a type definition as a JSON code block | Unused | | `Steps` / `Step` | Auto-numbered sequential walkthrough | In use — ordered procedures in guides | | Tables (markdown) | Rows and columns, optional sticky header | Native markdown — genuine enumerations only (see `mdx-style`) | -| `Tabs` / `Tab` | Tabbed views with language synchronization | In use — switchable non-code variants; surface switching in guides (`REST` / `WebSocket (Relay)`) with a `groupId` so the reader's choice persists down the page | +| `Tabs` / `Tab` | Tabbed views with language synchronization | In use — switchable non-code variants. In guides, surfaces get `###` headings, not tabs; the one exception is a step inside `` (`mdx-capability-guides`) | | `Tooltip` | Contextual info on hover, for text and code | Rare (×6) — only where it genuinely helps | | `Versions` | Content that switches with a version dropdown | Unused | | `WebhookPayloadSnippet` | Embeds a webhook payload example from the API Reference | In use | diff --git a/.claude/rules/mdx-diagrams.md b/.claude/rules/mdx-diagrams.md index 22774e7565..e6e09dada8 100644 --- a/.claude/rules/mdx-diagrams.md +++ b/.claude/rules/mdx-diagrams.md @@ -9,10 +9,8 @@ paths: ## Themed SVG diagrams Applies to hand-authored SVG diagrams under `fern/assets/images/img/`. -Exemplar: `ai-agent-flow-themed.svg`. Sequence-diagram exemplars from the -capability guides (pending #660, #692, #693): `outbound-call-lifecycle-themed.svg`, -`outbound-call-relay-lifecycle-themed.svg`, `call-stream-lifecycle-themed.svg`, -`machine-detection-flow-themed.svg`. +Exemplar: `ai-agent-flow-themed.svg`; for sequence diagrams, +`outbound-call-lifecycle-themed.svg` and `machine-detection-flow-themed.svg`. ### One file, both modes