---
name: generate-doc
description: Use for OpenIAP documentation generation work, especially the release-note card each PR carries in packages/docs/src/pages/docs/updates/releases.tsx, written as already published with the expected native and framework versions and their future GitHub Release links, updating an existing unreleased train instead of creating a duplicate.
---

# Generate OpenIAP Docs

Use this skill when the user asks to generate or update OpenIAP docs, and for
the release card every PR into `main` that changes a published package carries.

## Required Reading

Before editing docs, read:

- `AGENTS.md` or `CLAUDE.md`
- `packages/docs/CONVENTION.md`
- `knowledge/internal/05-docs-patterns.md`
- `knowledge/internal/06-git-deployment.md`
- `knowledge/internal/07-docs-consistency.md`

If the task also changes package/library behavior, use `openiap-workflows` and
read the package or library convention file before editing that code.

## Release Note Mode

A PR into `main` writes its release card before the release, as already
published; "Docs Ship With The Change" in
`knowledge/internal/05-docs-patterns.md` is the canonical rule.

Use `knowledge/internal/05-docs-patterns.md#release-note-completeness-gate`
to inventory the full PR and selected release train. Complete the gate after
writing or updating the card, including after scope changes.

Stable and RC releases share `main`. Keep the eventual stable release card
with its source PR; do not add a duplicate card for each npm `next` publication.
Vercel automatically deploys main's docs, including RC metadata and cards
ahead of package publication.

Write every card this way:

- Use `Package Releases`, not `Planned Package Releases`.
- Link expected release/package URLs exactly as the release will publish them.
- Do not add `(planned)` labels.
- Mention the release as publishing or shipping, not as upcoming.
- State in your response that the links are expected release links until actual
  deployment is complete.

After the train publishes, compare each version and link with the published
releases and correct only what differs.

## Existing Unreleased Train

Before adding a release card, inspect the newest entries and package tags.

- If an existing card describes a release train whose package tags are not all
  published yet, update that card in place with the new user-visible changes.
  Do not add another card for the same train.
- Consolidate overlapping unreleased cards when they describe the same package
  versions. Preserve their old IDs in the note's pagination-aware alias list
  and render matching hidden anchors so old deep links select the right page.
- If an unreleased hosted IAPKit card already exists and companion SDK packages
  belong to the same release train, expand that card into the single
  consolidated release entry and advance its date and title. Do not create a
  second card for the package versions.
- Use only sections with distinct user-visible behavior or required action.
  Keep any shared summary first, affected native and framework notes next,
  migration or integration action after them, and `Package Releases` at the
  bottom (see Editing Release Notes).
- IAPKit and its MCP deploy as services and have no package version. Include
  their user-visible behavior in the consolidated card, but never invent an
  IAPKit item in the versioned `Package Releases` list.
- Create a new card only when the latest card is already fully published or the
  new work has an explicitly separate release train.

## Version Sources

Never infer framework versions from adjacent release notes or from
`openiap-versions.json`. Read the metadata paths and tag formats from
`knowledge/internal/06-git-deployment.md#release-docs-version-guard`, including
the independently versioned Client Protocol, Commerce Protocol, and CLI. Do
not derive their versions from native or framework releases.

When workflows will bump versions after the docs are written, resolve expected
versions in this order:

1. Reuse explicit targets already recorded in the existing unreleased card or
   release plan.
2. Use package metadata that has already advanced beyond the last published
   release.
3. For an affected package still at its last published stable version, classify
   the public change before resolving its target: use the next patch for
   backward-compatible fixes, the next minor for backward-compatible features,
   and the next major for breaking public API or type removals. If the SemVer
   impact is ambiguous or conflicts with an existing release plan, ask instead
   of guessing.
4. Do not bump unaffected packages merely to make a release list symmetrical.
5. Reuse the explicit maintainer-selected Client Protocol target from the
   coordinated release plan or unreleased card. If no explicit target
   exists, ask; never infer one. The note reports the `clientProtocol` value
   actually committed in `openiap-versions.json`, which mirrors
   `specs/client/package.json` — so a plan naming a target the manifest does not
   carry is a stop-and-ask, not a value to compute your way out of.

Before naming any package's next major, inspect the canonical deprecation and
migration schedule. The release train must include every public removal already
scheduled for that major, or stop for maintainer direction to reschedule the
contract; never announce a major while claiming APIs scheduled for that major
remain available.

Write every resolved target into the release card with its expected tag link.
Do not leave versionless package bullets, `(planned)` labels, or a
`Planned Package Releases` list. Ask for confirmation
only when repository evidence names conflicting target versions or it is unclear
whether work belongs to the existing train.

## Editing Release Notes

Release notes live in:

`packages/docs/src/pages/docs/updates/releases.tsx`

Follow the existing card pattern:

- Add the newest note near the top of `allNotes`.
- Use a stable kebab-case `id` with the date.
- Use `new Date('YYYY-MM-DD')`.
- Use `AnchorLink` for the heading.
- Keep package links in a `Package Releases` list.
- Name the expected version once per package behavior group and in the linked
  `Package Releases` list.
- Link issues and PRs when they exist.
- Do not edit `packages/docs/src/generated/version-metadata.json` manually; it
  is produced by `./scripts/sync-versions.sh`.
- Register the card's package tags as aliases and render their hidden anchors:

  ```tsx
  aliases: MY_RELEASES.map((release) => release.tag),
  // and, first thing inside the card's <div>:
  {MY_RELEASES.map((release) => (
    <span key={release.tag} id={release.tag} aria-hidden="true" />
  ))}
  ```

  Release workflows link a version's own anchor
  (`/docs/updates/releases#godot-iap-3.5.1`). The page paginates and resolves a
  hash only against a note's `id` or `aliases`, so a card without them leaves
  those links on page one — a dead link that still looks alive. `bun run
audit:docs` fails when a card lists `Package Releases` without them.

Card section layout (mandatory for multi-package cards):

- Use only the sections that contain distinct user-visible behavior or
  information readers must act on. When present, keep this order:
  1. `Common changes`
  2. `Protocols and native packages`
  3. `Framework libraries`
  4. `Integration notes` (or migration notes)
  5. The bordered `Package Releases` block
- Never add one `h5` heading per platform or framework (no `Apple`, `Google`,
  `React Native`, `Expo`, ... headings). Use one parent `<li>` per package,
  following the package-specific grouping rule in
  `knowledge/internal/05-docs-patterns.md`: put `<strong>package version</strong>`
  once, then one inline change or a nested `<ul>` for multiple changes.
  Never repeat the package/version label on each nested change.
- `Protocols and native packages` holds `Client Protocol`, `Commerce Protocol`,
  `openiap-apple`, and `openiap-google` bullets; `Framework libraries` holds the
  framework SDK bullets. Omit the section when it would be empty.
- A package whose only change is selecting a shared native dependency,
  regenerating types, or republishing the same behavior belongs only in
  `Package Releases`. Do not manufacture one boilerplate bullet per wrapper.
- One bullet may name multiple packages when the same user-visible behavior and
  caveats apply to all of them. Keep distinct changes in separate nested
  bullets within their package group.
- The July 29, 2026 card (`openiap-major-api-cleanup-2026-07-29`) and the
  August 4, 2026 card (`amazon-rvs-user-data-patch-train-2026-08-04`) are the
  reference implementations of this layout.

## Reader-First Writing Standard

Apply the canonical standard in
`knowledge/internal/05-docs-patterns.md#reader-first-writing-standard` to every
new or edited release card. Render the result and remove repeated facts,
wrapper-only dependency boilerplate, and sections with no reader action.

## Multi-package Release Trains

The consolidated release page remains the release-note SSOT, but a release that
ships several packages must still be readable package by package. This is the
project decision recorded from issue #206.

- When the user gives a starting commit, inspect that commit inclusively through
  the latest target branch, then include the current PR diff. Do not derive the
  release contents only from the PR title or its latest commits.
- Group notable changes under the affected platform package or framework
  library, and the independently released protocols, CLI, or hosted service
  when affected. Omit groups with no user-facing change.
- Apply the Reader-First Writing Standard above. State the behavior users gain
  or the regression that was fixed; do not list commit mechanics,
  version-bump-only commits, generated files, or repeated cross-framework
  boilerplate.
- Put truly shared schema or release-process changes in one short shared group,
  then describe framework-specific wiring or caveats in the relevant framework
  group.
- Link the issues and PRs that explain user-visible fixes. Keep package-local
  changelogs as pointers to this canonical entry unless a registry requires an
  inline changelog.

## Validation

For docs-only release-note edits, run:

```bash
# Subshells: a bare `cd` would leave the next line inside packages/docs, where
# the second `cd` fails and the root audits do not resolve.
set -e
(cd packages/docs && bunx prettier --check "src/**/*.{ts,tsx,js,jsx,css,json}")
(cd packages/docs && bun run build)
bun run audit:docs
bun run audit:release-state
git diff --check
```

If Prettier fails, format only the touched docs files and rerun the checks.

Before committing to `main`, pull first:

```bash
git pull --ff-only origin main
```
