Skip to content
Merged
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
95 changes: 95 additions & 0 deletions .claude/skills/sdk-docs-update/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
---
name: sdk-docs-update
description: Release-driven update of the Libra SDK docs. Scans what shipped in @libra/sdk (LibratechAI/sandbox packages/sdk) since the docs' synced version, regenerates the changelog, updates the affected pages from the SDK README, refreshes the screenshots/videos those pages use, runs the structural and visual gates, and opens a PR. Use when the user says "update the SDK docs", "what shipped in the SDK", "sync the SDK changelog", or a new SDK version is out.
---

# SDK docs update

One pass per release (or batch of releases): **scan → sync changelog → update pages → capture/render assets → check → PR.** Everything derives from `LibratechAI/sandbox` `packages/sdk/` (`README.md` = API contract, `CHANGELOG.md` = what shipped). Read [CLAUDE.md](../../../CLAUDE.md) first for the source-of-truth and page conventions.

## Prerequisites

- `gh auth status` OK (the sandbox is private). Node 22 on PATH (`/opt/homebrew/opt/node@22/bin`).
- `Screenshots/node_modules` installed and `npx playwright install chromium` done (see [Screenshots/README.md](../../../Screenshots/README.md)).
- For `auth: true` captures: a fresh `Screenshots/auth/storageState.json` — `cd Screenshots && npm run auth`. **This is the only manual gate**; skip it when no page needs new authenticated shots.

## Steps

### 1. Scan
```bash
git checkout main && git pull
node scripts/release-scan.mjs # releases newer than snippets/sdk-version.mdx, bullets bucketed by page, merged SDK PRs
```
`Nothing new` → stop. Otherwise note every version, every **BREAKING** bullet, and the **Unreleased** block (merged, not shipped: never document it).

### 1b. Never document a claim you have not checked against the code

The SDK README and the WK product decks both go stale. On 2026-08-27 a 13-topic verification pass over the "SDK v2" deck returned 74 findings: only 11 CONFIRMED, 13 outright OUTDATED, 47 partly wrong. Things that would have shipped as fact: a `622px` breakpoint that exists nowhere in the codebase, Deep Thinking described as a paid-tier feature (it is not gated at all), and a chat-mode selector that is now a combined tier+model control.

So, for any claim sourced from a deck, a ticket, a Slack message, or the README rather than from code you just read:

- Verify it against `LibratechAI/sandbox` `main` before it reaches a page. `gh api "repos/LibratechAI/sandbox/git/trees/main?recursive=1" --jq '.tree[].path'` then read the files.
- Separate **SDK-package behaviour** (`packages/sdk/`) from **main-app behaviour rendered inside the iframe** (`src/`). They have different release cycles and the distinction changes what an integrator can rely on.
- Treat flag-gated or entitlement-gated behaviour as **not general**. If it sits behind a feature flag, a per-team grant, or an env var, either say so explicitly or leave it out.
- Never publish: internal flag names, M2M credentials, unreleased work, or the ops mechanics behind a restriction.
- Attribute design guidance that is not in the code as guidance ("Wolters Kluwer design guidance, not enforced by the SDK").

For a batch of claims this is worth fanning out — one agent per topic, each returning `{claim, verdict, evidence, corrected, docsRelevance}`, with a second agent adversarially re-checking every CONFIRMED/OUTDATED verdict. The adversarial pass is what caught the flag-gating and the SDK-vs-app confusions above.

### 2. Diff the contract
The changelog says *what* changed; the README says *how it now works*. Pull the README changes in the window and read them:
```bash
gh api "repos/LibratechAI/sandbox/commits?path=packages/sdk/README.md&since=<baseline release date>T00:00:00Z" --jq '.[].sha' \
| xargs -I{} gh api repos/LibratechAI/sandbox/commits/{} --jq '.files[] | select(.filename=="packages/sdk/README.md") | .patch'
```
For bullets the scan could not bucket, `gh pr view <#> --repo LibratechAI/sandbox` on the listed PRs.

### 3. Sync the changelog
```bash
git checkout -b docs/sdk-v<latest>
node scripts/sync-changelog.mjs # rewrites changelog.mdx + snippets/sdk-version.mdx
git add changelog.mdx snippets && git commit -m "changelog: sync to v<latest>"
```

### 4. Update pages
For each `## By docs page` bucket from step 1:
- **README text or snippet changed** → update the page; code that exists in the README is copied verbatim, not paraphrased. `grep -rn "<old name>" --include=*.mdx .` to catch every occurrence of a renamed option/slug/type.
- **New `init()` option or `Libra.*` method** → `reference/api.mdx` (table/section) + the guide that owns the behaviour + `reference/types.mdx` if a type changed.
- **New product/source/locale** → `capabilities/products-and-sources.mdx` or `guides/locales.mdx`, and the type unions in `reference/types.mdx`.
- **BREAKING** → make sure the migration is stated on the affected page (old → new) in addition to the tagged changelog entry.
- **Pure UI change** (no API) → `capabilities/overview.mdx` prose if a user would notice; otherwise changelog only.
- **New page** → add it to `docs.json` navigation and to `PAGE_MAP` in `scripts/release-scan.mjs`.

Internal-only detail (SPEC.md internals, M2M credentials, roadmap, security specifics) never goes in. Keep content commits separate from asset commits.

### 5. Assets for changed pages
For every page edited in step 4, list its images/videos and its TODO markers:
```bash
grep -oE '(src="/assets/[^"]+"|TODO (screenshot|video): [a-z0-9-]+)' <page>.mdx
```
- **Existing shot whose UI changed** → `cd Screenshots && npm run capture -- --id <id>`; fix selectors in `manifest/shots.yaml` if the playground moved (prefer ids/roles/text; `host:` for host-page controls).
- **New shot** → add a manifest entry (unique `id`, `output: assets/images/sdk/<subject>-<state>.png`, `page:`), put `{/* TODO screenshot: <id> */}` where the image goes, capture, then replace the marker with `<img src="/assets/images/sdk/…" alt="…" />`.
- **Videos** → `npm run render -- --id <scene>` (composed from stills by default); replace `{/* TODO video: <id> */}` with the `<video …>` tag from CLAUDE.md once the file exists.
- **Eyeball every new PNG/frame** (`npm run frames` for videos). A wrong selector produces a wrong picture, not an error. No splash, no spinner, no empty state.
```bash
npm run validate && npm run render:verify
git add assets Screenshots/manifest && git commit -m "assets: v<latest> screenshots/videos"
```

### 6. Gates
```bash
node scripts/check.mjs # nav ↔ files, links, assets, remaining TODO worklist
node scripts/sync-changelog.mjs --check # generated files are current
cd Screenshots && npm run check:serve # every changed page renders: no 404, broken image, unplayable video, console error
```
Fix, don't skip. Remaining `TODO screenshot/video` markers are allowed on a PR only if listed in the PR body as follow-up work.

### 7. PR
```bash
git push -u origin docs/sdk-v<latest>
gh pr create --title "docs: SDK v<latest>" --body "<versions covered · pages touched · BREAKING migrations stated · assets refreshed · open TODO markers>"
```
Mintlify deploys `main` on merge.

## Automating it
This skill is designed to run unattended except for step 5's authenticated captures. A scheduled routine (Claude Code `/schedule`, weekly) can run steps 1–4, 6, 7 and leave the capture worklist (the `TODO` markers `check.mjs` prints) for a local run of step 5, the same handoff the documentation repo uses.
14 changes: 14 additions & 0 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: check
on:
pull_request:
push:
branches: [main]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
- run: node scripts/check.mjs
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.DS_Store
**/.DS_Store
# Claude Code local state
.claude/worktrees/
.claude/settings.local.json
# capture harness has its own .gitignore (node_modules, auth session, scratch)
16 changes: 9 additions & 7 deletions .mintignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# Mintlify automatically ignores these files and directories:
# .git, .github, .claude, .agents, .idea, node_modules,
# README.md, LICENSE.md, CHANGELOG.md, CONTRIBUTING.md

# Draft content
drafts/
*.draft.mdx
# Excluded from the published Mintlify build. Anything not listed is fetchable
# by URL even when unlinked (Mintlify also auto-ignores .git .github .claude
# node_modules README.md LICENSE.md CHANGELOG.md — kept explicit anyway).
CLAUDE.md
README.md
LICENSE
scripts/
Screenshots/
.claude/
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
22
33 changes: 0 additions & 33 deletions AGENTS.md

This file was deleted.

76 changes: 76 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# CLAUDE.md

Mintlify docs site for the **Libra SDK** (`@libra/sdk`) — the embeddable chat widget Wolters Kluwer products integrate. Internal audience: WK product engineers first (code snippets, integration best practices), product managers and marketing second (capabilities, screenshots). English only; no translation pipeline (unlike `LibratechAI/documentation`).

## Source of truth

The SDK lives in `LibratechAI/sandbox`, `packages/sdk/`. The docs *derive* from it — never document behaviour that isn't in one of these:

- `packages/sdk/README.md` — the API contract and integration guidance. Guides and reference pages restate it; when they disagree, the README wins.
- `packages/sdk/CHANGELOG.md` — what shipped, per version (`## X.Y.Z - YYYY-MM-DD`). `## Unreleased` is merged-but-not-released: never document it.
- `packages/sdk/site/` — the hosted site at https://sdk.staging.libratech.ai (Docs / Changelog / Playground tabs). The playground is what we screenshot.

Do not publish internal-only detail: `packages/sdk/SPEC.md` internals (shims, bundling), M2M client ids/secrets, anything security-sensitive, roadmap, internal feature-flag names, or behaviour that is flag-gated/entitlement-gated rather than generally available. Releases are cut by a `chore(sdk): release vX.Y.Z` PR into `staging`; the Artifactory publish + WK CDN promotion follow automatically.

### The README is not always right

Verified 2026-08-27 against `main`: `packages/sdk/README.md` omits `LibraAuthErrorReason` and two of its five members, drops `'web_search'` from the printed `ResearchSourceId` union, never mentions the `baseUrl` origin validation, the init-twice no-op, the attach-target auto-detach, the `X-Libra-SDK-Version` header, or the embedding-origin allowlist, and describes SDK storage as living in "the iframe's localStorage" when the blob iframe inherits the **host page's** origin. Where these docs and the README disagree, the pages cite `packages/sdk/src` and say so. **When a page contradicts the README on purpose, leave the note explaining why** — otherwise a later sync will "fix" it back.

### Product decks are input, not truth

`Embedded Libra Assistant - SDK v2` (WK product deck, April 2026) is the source for host-side UX guidance — panel placement, the persistent "Ask Libra" entry point, the promo card, the 480px default width. It is **partly stale on product behaviour**: its "Standard / Deep Thinking / Fast" chat-mode selector is now a combined tier+model control, Deep Thinking is not tier-gated, its `622px` label breakpoint does not exist in the codebase, and its PRIVATE/TEAM project tags are now access pills. Every deck claim that reached these docs was checked against `LibratechAI/sandbox` first. Attribute design guidance that is not in the code explicitly ("Wolters Kluwer design guidance ... not enforced by the SDK") rather than stating it as SDK behaviour.

### Naming

`@libra/sdk` is the package; **the embedded widget** is the surface. WK material calls that surface the **Libra Add-in** — these docs avoid "add-in" because in the Libra codebase it means the **Word/Outlook add-ins**, a separate surface with its own chat list. Say so once per page where confusion is likely, and never use "add-in" unqualified.

## Generated files — don't hand-edit

- `changelog.mdx` and `snippets/sdk-version.mdx` — `node scripts/sync-changelog.mjs` (reads `CHANGELOG.md` from `origin/main` via `gh api`; `--file <path>` for a local copy; `--check` exits 1 if stale).

Pages show the current version with `import { sdkVersion } from '/snippets/sdk-version.mdx'` → `{sdkVersion}`. Inside code blocks keep the literal `<VERSION>` placeholder, as the README does.

## Commands

```bash
mint dev # local preview, http://localhost:3000 (Node 22; see ~/.local/bin/mint)
node scripts/check.mjs # structural gate: nav ↔ files, links, assets, TODO-screenshot worklist
node scripts/sync-changelog.mjs # regenerate changelog.mdx + version snippet
node scripts/release-scan.mjs # what shipped since the docs' synced version, bucketed by page
cd Screenshots && npm run auth # headed login on the playground → auth/storageState.json (manual gate)
cd Screenshots && npm run capture -- --all # screenshots per manifest/shots.yaml → assets/images/sdk/
cd Screenshots && npm run render -- --all # videos per manifest/storyboards.yaml → assets/videos/
cd Screenshots && npm run check:serve # render every changed page on mint dev; fails on broken img/video/404
```

There is no build/test suite beyond these. Mintlify deploys `main` automatically via the GitHub app.

## Page conventions

- Frontmatter: `title`, `description` (sentence, ends without a period is fine), optional `sidebarTitle`.
- Audience split: `getting-started/` + `guides/` + `reference/` are for engineers; `capabilities/` is for product managers and marketing and must stay free of code. `guides/panel-and-placement` is the one page written for both — it is the host-side UX contract.
- Second person, sentence-case headings, language tag on every code block. Code that exists in the README is copied verbatim, not paraphrased.
- Developer pages lead with the snippet; PM/marketing pages (`capabilities/`) lead with what the end user sees.
- Screenshots live in `assets/images/sdk/<subject>-<state>.png` and are referenced as `<img src="/assets/images/sdk/…" alt="…" />`. A shot that is planned but not yet captured is a `{/* TODO screenshot: <manifest-id> */}` comment on the page and an entry in `Screenshots/manifest/shots.yaml` — `node scripts/check.mjs` lists them as the capture worklist. Never reference an image that doesn't exist.
- Header videos: `<video autoPlay muted loop playsInline src="/assets/videos/<slug>.webm" className="rounded-xl border w-full" />`, rendered from a `Screenshots/manifest/storyboards.yaml` scene. Until rendered, the page carries `{/* TODO video: <scene-id> */}` instead (also listed by `check.mjs`).
- Internal links are root-relative (`/guides/embedding`); Lucide icons.
- In MDX prose, `{`, `}` and a bare `<` are JSX — keep them inside backticks or escape them.

## Screenshots

`Screenshots/` is the Playwright capture harness ported from the documentation repo (`Screenshots/README.md`). Target is the staging playground; selectors resolve *inside the SDK iframe* by default, `host:` prefix targets the host page. Authenticated shots need `npm run auth` first — the one manual gate (Auth0 popup). Every manifest `output` must be referenced by a page (`npm run validate`).

Two traps that cost a full re-capture on 2026-08-27, both now guarded in-file:

- **`manifest/shots.yaml` order is load-bearing.** `locale-de` calls `Libra.setLocale('de-DE')`, which the SDK persists to `localStorage`; because the runner reuses one browser context per auth flag, every later auth shot came out in German. It runs last, and `chat-hero` (the session it chains from) second-to-last.
- **`mint dev` does not hot-reload `docs.json` navigation.** A leftover dev server serves a stale sidebar while pages themselves look fine, so a `check:serve` run can pass while showing the wrong nav. `visual_check.ts` now kills the whole process group; if the sidebar looks wrong, check for a stray listener on 3000-3005 before believing it.

**Eyeball every new PNG and video frame.** A wrong selector yields a wrong picture, not an error — this is how the German-locale leak was found. Never ship a splash, spinner, or empty state as a result shot: `chat-hero` deliberately asks a live question and waits for `.cited-sources-button` plus a rendered Regenerate action.

## Keeping the site current

`.claude/skills/sdk-docs-update/SKILL.md` is the release-driven pipeline: scan → sync changelog → update the affected pages → capture/render assets → check → PR. Use it when the user says "update the SDK docs", "what shipped in the SDK", or a new SDK version is out.

## Git

Branch from `main` (`docs/sdk-vX.Y.Z` for release updates), open a PR, never `--no-verify`. Keep content commits and asset commits separate.
Loading
Loading