Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .claude/agents/tsdoc-auditor.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,11 @@ Confirm clean before reporting.

Before auditing a specific symbol type, read the relevant guide:

| Symbol type | Guide |
| ----------- | ------------------------------------ |
| Hooks | `.claude/tsdoc-guides/hooks.md` |
| Components | `.claude/tsdoc-guides/components.md` |
| Flows | `.claude/tsdoc-guides/flows.md` |
| Symbol type | Guide |
| ----------- | ---------------------------------- |
| Hooks | `.claude/doc-guides/hooks.md` |
| Components | `.claude/doc-guides/components.md` |
| Flows | `.claude/doc-guides/flows.md` |

## ESLint compliance

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ Every exported React component's `@remarks` must include an events table listing

## @example

- **Skip** for components documented in `docs/workflows-overview/` — that page is the canonical integration example. Add a `@see` tag pointing to it instead.
- **Include** for standalone blocks and utilities without a workflow page.
Include for all public block components. For components typically consumed via a flow rather than standalone, a minimal usage example (component with required props) is still useful — it shows the basic API without requiring full flow context.

## Release tags

Expand Down
51 changes: 51 additions & 0 deletions .claude/doc-guides/docs-shared.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Docs site — shared reference

Canonical facts shared by the docs skills (`docs-check`, `docs-regen`,
`docs-change-ia`) so they don't drift. Read whichever skill matches your task.

## npm scripts (run from the repo root)

All docs scripts live in the **root** `package.json` and delegate into `docs-site/`.

| Script | What it does |
| --------------------------- | ------------------------------------------------------------------------------ |
| `npm run docs` | Docusaurus **dev server** (hot reload) at http://localhost:3000 |
| `npm run docs:install` | Install `docs-site/` deps (`npm install` inside `docs-site/`) |
| `npm run docs:build` | **Production build**. Throws on any broken link/anchor — the real gate |
| `npm run docs:lint` | Verify every `docs/**/*.md` has `title` + `description` frontmatter |
| `npm run docs:serve` | Serve the already-built production site locally |
| `npm run docs:api:generate` | Regenerate **reference docs only** (TypeDoc). Fast iterate path |
| `npm run derive` | Full derived pipeline: SDK build + endpoint inventory + api report + reference |

Any script that builds or generates needs `docs-site/` deps installed first:

```bash
test -d docs-site/node_modules || npm run docs:install
```

## Two kinds of content — know which you're touching

Get this wrong and edits either don't stick or break the build.

| | **Hand-authored** | **Auto-generated reference** |
| ------- | -------------------------------------- | ----------------------------------------------------- |
| Where | `docs/**` _except_ `docs/reference/**` | `docs/reference/**` only |
| Source | the `.md`/`.mdx` file itself | TSDoc in `src/` + reference config |
| Edit by | editing the file | editing TSDoc + `router.config.ts`, then regenerating |
| Sidebar | listed in `docs-site/sidebars.ts` | `{ type: 'autogenerated', dirName: 'reference' }` |

**Never hand-edit a file under `docs/reference/`** — the next regeneration overwrites it
(its frontmatter carries `generated_by: typedoc`, `custom_edit_url: null`). To change
generated content or structure, use `docs-regen` and `docs-change-ia` → `REFERENCE.md`.

The dev server hot-reloads hand-authored content only; reference changes need
regenerating.

## Publishing and writing rules

Source of truth is **this repo** (`embedded-react-sdk`); a release workflow builds and
publishes to `sdk.gusto.com`. All work is local and previewed first.

These docs are **partner-facing**. The writing rules (second person, don't speculate
about the reader's app, no internal `@/` import aliases in code samples) live in the
root `CLAUDE.md` under **Documentation** — follow them there.
46 changes: 42 additions & 4 deletions .claude/tsdoc-guides/flows.md → .claude/doc-guides/flows.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,58 @@ Flows are the primary user of the `@components` block tag — it documents the b

Place it after `@remarks` and before `@param`/`@returns` (the `tsdoc-sort-tags` rule enforces this grouping). Link names must resolve to documented exports for the table to link them; unresolved names still render as plain text rows.

**Correctness rules — get these wrong and the table is misleading:**

- **List what the machine actually renders.** Trace the `*Components.tsx` return statements. Do not list an umbrella name that is itself a standalone `<Flow>` the machine never directly mounts.
- **Wrapper flows collapse.** If the flow composes another separately-documented flow as a step (e.g. `OnboardingExecutionFlow`), list just that sub-flow as one entry — not its internal steps. The sub-flow's own `@components` owns its children.
- **Cross-namespace nodes need the namespace.** `{@link EmployeeOnboarding.OnboardingExecutionFlow}`, not just `{@link OnboardingExecutionFlow}`.
- **The diagram and `@components` must agree.** If the GUIDE.md diagram collapses a sub-flow to one node, the `@components` list must match.

## @example

Flows are always documented in `docs/workflows-overview/` — skip `@example` in TSDoc. Use a `@see` tag pointing to the relevant overview page.
Every flow needs a `@example`. Examples live in TSDoc as the source of truth.

**Hub/loop flows** (resting list with no terminal state, e.g. `PayrollFlow`, `EmployeeListFlow`) — show minimal wiring; `onEvent` is optional but useful for showing navigation events:

````ts
@example
```tsx
<PayrollFlow
companyId={companyId}
onEvent={(event) => {
if (event.eventName === 'payroll/exit') navigate('/dashboard')
}}
/>
```
````

**Guided flows** (linear steps with a real exit, e.g. `OnboardingExecutionFlow`, `TerminationFlow`) — `onEvent` is **required** in the example and must handle the exit event by name. A guided flow example that only shows `onDone` is incomplete:

````ts
@example
```tsx
<OnboardingExecutionFlow
employeeId={employeeId}
onEvent={(event) => {
// The flow exits when the partner navigates away or cancels
if (event.eventName === 'employee/onboarding/done') navigate('/employees')
}}
/>
```
````

Pull the exact exit event string from `src/shared/constants.ts` componentEvents — don't guess. If the exit event bubbles via `onEvent` without a machine transition (the machine ignores it), note that in a comment in the example.

## What requires product context

The business intent behind a flow's sequence (why these blocks, why this order) often isn't deducible from block names alone. Sources:

1. `docs/workflows-overview/` — typically has the narrative
1. The GUIDE.md colocated with the flow component — typically has the narrative
2. Jira epic or feature ticket in the current conversation
3. Ask the human if the workflow page is missing or incomplete
3. Ask the human if both are missing or incomplete

Do not invent the purpose of a flow from its block structure.

## Long-form prose: GUIDE.md

Keep `@remarks` to concise observable behavior. Extended narrative for a flow — walkthroughs, sequencing rationale, integration notes — belongs in a `GUIDE.md` colocated in the flow's source directory; the doc engine slots it into the generated reference page. `GUIDE.md` is authored prose (Prettier-exempt), distinct from generated content. Don't migrate prose into `GUIDE.md` speculatively — the authoring conventions are still being established in the guide-content PRs; write the TSDoc, and leave narrative to the human unless directed otherwise.
Keep `@remarks` to concise observable behavior. Extended narrative for a flow — walkthroughs, sequencing rationale, integration notes — belongs in a `GUIDE.md` colocated in the flow's source directory; the doc engine slots it into the generated reference page. `GUIDE.md` is authored prose (Prettier-exempt), distinct from generated content.
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ This is not inferrable from the hook alone. Read `fields.tsx` before writing `@r

## Usage patterns

Two integration patterns exist — use the one that matches `docs/workflows-overview/` for this hook:
Two integration patterns exist — use the one that matches existing usage in `src/components/` or the flow's GUIDE.md for this hook:

**`SDKFormProvider` pattern** — partner wraps a group of fields from one hook:

Expand Down Expand Up @@ -71,7 +71,7 @@ These cannot be inferred from code alone and need human context before writing `
**Sources in priority order:**

1. Jira ticket or PR description in the current conversation
2. `docs/workflows-overview/` for established patterns
2. GUIDE.md colocated with the flow that uses this hook
3. MCP: Jira, Confluence, or Notion if linked
4. Ask the human — do not invent business rules

Expand All @@ -83,4 +83,4 @@ If none are available, write the structural parts (`@param`, `@returns`, release

## Long-form prose: GUIDE.md

Like flows, each hook directory can carry a `GUIDE.md` whose authored prose the doc engine slots into the hook's generated reference page. Keep `@remarks` to concise observable behavior; longer walkthroughs go in `GUIDE.md`. Don't create or backfill `GUIDE.md` speculatively — its authoring conventions are still being established in the guide-content PRs.
Like flows, each hook directory can carry a `GUIDE.md` whose authored prose the doc engine slots into the hook's generated reference page. Keep `@remarks` to concise observable behavior; longer walkthroughs go in `GUIDE.md`. Don't create or backfill `GUIDE.md` speculatively without direction.
125 changes: 125 additions & 0 deletions .claude/skills/docs-change-ia/REFERENCE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# Auto-generated reference — internals

Read this before touching anything under `docs/reference/**`, the typedoc-custom plugin,
or `GUIDE.md` files. `SKILL.md` covers hand-authored content; this covers the generated
half.

## How reference docs are produced

TypeDoc reads TSDoc from `src/`, and a custom plugin (`docs-site/plugins/typedoc-custom/`)
routes each symbol into the `docs/reference/` tree. Output is committed but never
hand-written — including the `_category_.json` sidebar files the plugin emits.

Regenerate after any change to TSDoc, `router.config.ts`, or `typedoc.config.ts`:

```bash
test -d docs-site/node_modules || npm run docs:install # one-time prerequisite
npm run docs:api:generate # reference only (TypeDoc) — fast iterate path
npm run derive # full pipeline; use for a clean final pass
git diff docs/reference/ # confirm you changed only what you intended
```

## The reference IA config: `router.config.ts`

`docs-site/plugins/typedoc-custom/router.config.ts` is the single source of truth for
reference structure; its own TSDoc comments are the authoritative spec. Two exported
arrays matter, and **array order sets sidebar position** in both.

`DOMAINS` — the domain → namespace tree. Each entry is a top-level reference category
(e.g. **Employees**, **Payroll**):

- `label` — sidebar category label and the domain hub's H1.
- `path` — output slug **and** source-dir lookup (`employee` → `src/components/Employee`;
`time-off` → `src/components/TimeOff`).
- `namespaces` — namespaces under the domain, in render/sidebar order. Add one with
`{ id, subpath }` (id must match the namespace exported from `src/components/index.ts`).

`STANDALONE_PAGES` — symbols that don't belong to a domain, collected one page each
(currently `theme-variables`, `component-inventory`, `utilities`, `events`):

- `id` — output slug → `docs/reference/<id>.md`.
- `sources` — source-path fragments; a symbol routes here if its path contains any.
- `groups` — optional `@group` filter (e.g. `events` takes only the `Events` group).
- `displayName` — the page H1.

Reorder/relabel by editing the arrays, then regenerate.

## Ordering content within a generated page

You do **not** edit the generated `.md`. Order comes from two places:

- **`docs-site/typedoc.config.ts`** — `groupOrder` lists section names top-to-bottom (the
constants live in `docs-site/typedoc-utils.mjs`: `COMPONENT_GROUPS`, `HOOK_GROUPS`,
`COMPONENT_PROP_GROUPS`, `VARIABLE_GROUPS`); `sort: ['required-first', 'alphabetical']`
orders members within a group.
- **`@group` tags** in the TSDoc decide which section a symbol falls into.

So: reorder _sections_ via `groupOrder` / the group-name arrays; reorder _items within a
section_ via `sort` or names; move an item between sections by changing its `@group` tag.

## Injecting hand-authored prose into a generated page: `GUIDE.md`

To make authored narrative read as part of a generated hub/flow page, the plugin slots a
`GUIDE.md`'s sections into the page. Where it looks:

- **Domain hub** → `src/components/<Domain>/GUIDE.md` (Domain = PascalCase of the domain
`path`, e.g. `time-off` → `src/components/TimeOff/GUIDE.md`).
- **Flow page** → `GUIDE.md` beside the `*Flow` component's source.
- **Hook page** → `GUIDE.md` at the root of the hook's directory.

Format (parsed by `docs-site/plugins/typedoc-custom/utils.ts`): the leading `# H1` is
dropped as an author title; each `## section` is tagged with a slot comment.

```markdown
# My Guide (title — not rendered)

## Overview

<!-- slot: overview -->

Prose that lands at the top of the generated page.

## Notes

<!-- slot: appendix -->

Prose that lands at the bottom.
```

Recognized slots are **`overview`** and **`appendix`** (`GUIDE_SLOTS` in `utils.ts`).
Untagged/unknown-slot sections fall through to `appendix` with a build warning — content
is never silently dropped, but tag deliberately. Adding a slot means editing `GUIDE_SLOTS`
in `utils.ts` and the rendering in `theme.ts` — a code change beyond pure IA work, so
coordinate first.

## Grouping a generated reference page into a hand-authored sidebar section

The page is already emitted by the autogenerated **Reference** block, so naming it in a
second category duplicates it. Remove the duplicate via the `sidebarItemsGenerator` hook
in `docusaurus.config.ts` (which already does this for `reference/index`):

```ts
sidebarItemsGenerator: async ({ defaultSidebarItemsGenerator, ...args }) => {
const items = await defaultSidebarItemsGenerator(args)
return items.filter(
item =>
!(item.type === 'doc' && item.id === 'reference/index') &&
!(item.type === 'doc' && item.id === 'reference/theme-variables'), // ← relocated
)
},
```

Then add `'reference/theme-variables'` to the **Theming** category's `items` in
`sidebars.ts`. The page now renders once, under Theming. Verify with `npm run docs:build`.

## Verifying reference changes

```bash
npm run docs:api:generate # or npm run derive for a clean pass
git diff docs/reference/ # only the intended files changed
npm run docs:build # passes — broken links/anchors throw
```

If you change routing _logic_ (not just config), run the docs-site tests
(`npm --prefix docs-site test`); the router suite is
`docs-site/plugins/typedoc-custom/sdk-router.test.ts`.
101 changes: 101 additions & 0 deletions .claude/skills/docs-change-ia/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
---
name: docs-change-ia
description: >-
Reorganize the docs site's IA — move/split pages, reorder/relabel the sidebar,
restructure the reference. Use when a docs task touches docs-site/sidebars.ts,
page placement, or nav structure, or on "reorganize docs"/"move this page".
argument-hint: '[what you want to reorganize — e.g. "move theming guide next to the reference"]'
---

# Reorganize the docs-site IA

The docs site is a [Docusaurus](https://docusaurus.io) site. For the script map and the
hand-authored-vs-generated content model (which everything below hinges on), see
[`docs-shared.md`](../../doc-guides/docs-shared.md). Deep reference internals are in `REFERENCE.md` in
this folder. The docs are **partner-facing** — follow the writing rules in the root
`CLAUDE.md`.

**Never hand-edit a file under `docs/reference/`** — it's regenerated and your changes
are overwritten. To change generated content or structure, see `REFERENCE.md`.

## Where things live

- `docs/` — all content (hand-authored markdown + generated reference output).
- `docs-site/sidebars.ts` — the sidebar tree. **This is your main lever.**
- `docs-site/docusaurus.config.ts` — site config, **navbar/footer links**. These are
hardcoded `to:` paths; if you move a page they point to, update them here too.
- `docs-site/plugins/typedoc-custom/router.config.ts` — the reference IA (see `REFERENCE.md`).

## Verify before you're done

```bash
npm run docs:lint # frontmatter valid (title + description on every page)
npm run docs:build # production build — REQUIRED; broken links/anchors throw
```

`docs:build` is non-negotiable: the dev server tolerates broken links, the build (and
the release build) does not. If you touched reference _structure_, also run
`npm run derive` first — see `REFERENCE.md`.

## Common tasks

### Create a standalone page

Create `docs/<section>/<slug>.md` with required frontmatter (both fields, or lint/build
fails):

```markdown
---
title: Your Page Title
description: One-sentence summary — shows in search results and link previews.
---
```

Register it in `docs-site/sidebars.ts` by doc id (path under `docs/`, no extension), or
the object form for a custom sidebar label:

```ts
items: [
'getting-started/quick-start',
'getting-started/your-new-page', // by id
{ type: 'doc', id: 'getting-started/your-new-page', label: 'Short label' },
]
```

### Move content into a standalone page

1. Create the new page (above) and cut the content over.
2. **Fix every inbound link** — relative markdown links, navbar/footer `to:` paths in
`docusaurus.config.ts`, cross-references in other pages. Broken links fail the build.
3. Add the new page to `sidebars.ts`.

### Reorder or relabel sidebar items

Edit `docs-site/sidebars.ts`. Order within a category's `items` array **is** the sidebar
order; labels come from `label`; categories take `collapsed` / `collapsible`. Items under
the **Reference** category are auto-generated — their order/labels are set in `REFERENCE.md`.

### Group hand-authored pages with auto-generated reference

A category's `items` can mix manually listed docs with the autogenerated block. Two ways:

- **Sidebar grouping** — put a hand-authored page and a generated reference page in the
same category. ⚠️ The generated page is _already_ in the `{ type: 'autogenerated' }`
block, so listing it again makes it appear twice; drop the duplicate via the
`sidebarItemsGenerator` hook in `docusaurus.config.ts` (worked example in `REFERENCE.md`).
- **Page-level prose injection** — fold hand-authored prose _into_ a generated hub/flow
page via a `GUIDE.md` slot, so narrative and generated API tables read as one page
(mechanism in `REFERENCE.md`).

If you only need the pages _near_ the reference (not merged), sidebar grouping is simpler
— or just reorder the top-level categories so e.g. **Theming** sits next to **Reference**.

### Order content within a generated reference page

Pages like `reference/theme-variables` are generated; on-page order comes from TypeDoc
config and `@group` tags in source, not from editing the page. See `REFERENCE.md`.

---

Read `REFERENCE.md` before any task touching `docs/reference/**`, `router.config.ts`, or
`GUIDE.md` files.
Loading
Loading