name: API Reference Sync on: workflow_dispatch: pull_request: types: [closed] permissions: actions: read contents: write pull-requests: write issues: write repository-projects: write id-token: write concurrency: group: api-reference-sync-${{ github.event.pull_request.number || github.run_id }} cancel-in-progress: true jobs: # ─── Job 0: Lightweight shell gate ─────────────────────────────────── analyze: if: github.event_name == 'workflow_dispatch' || (github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'main') runs-on: ubuntu-latest outputs: has_changes: ${{ steps.check.outputs.has_changes }} pr_number: ${{ steps.context.outputs.pr_number }} pr_title: ${{ steps.context.outputs.pr_title }} pr_url: ${{ steps.context.outputs.pr_url }} pr_body: ${{ steps.context.outputs.pr_body }} steps: - name: Checkout code uses: actions/checkout@v5 - name: Set PR context id: context env: PR_TITLE: ${{ github.event.pull_request.title || '' }} PR_BODY: ${{ github.event.pull_request.body || '' }} run: | echo "pr_number=${{ github.event.pull_request.number || '' }}" >> "$GITHUB_OUTPUT" echo "pr_title=$PR_TITLE" >> "$GITHUB_OUTPUT" echo "pr_url=${{ github.event.pull_request.html_url || '' }}" >> "$GITHUB_OUTPUT" EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64) echo "pr_body<<$EOF" >> "$GITHUB_OUTPUT" echo "$PR_BODY" >> "$GITHUB_OUTPUT" echo "$EOF" >> "$GITHUB_OUTPUT" - name: Check for API-relevant changes id: check env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then echo "has_changes=true" >> "$GITHUB_OUTPUT" echo "workflow_dispatch — full scan" exit 0 fi CHANGED=$(gh pr diff ${{ github.event.pull_request.number }} --name-only || true) if echo "$CHANGED" | grep -qE '^packages/(core/src/core/(ui|selectors)|html/src/ui|react/src/ui|store/src)/'; then echo "has_changes=true" >> "$GITHUB_OUTPUT" echo "API-relevant files changed" else echo "has_changes=false" >> "$GITHUB_OUTPUT" echo "No API-relevant changes detected" fi # ─── Job 1: Reference copy drift (Claude Opus) ────────────────────── docs-reference-copy: needs: analyze if: needs.analyze.outputs.has_changes == 'true' runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v5 with: fetch-depth: 0 - name: Check reference copy drift uses: anthropics/claude-code-action@v1 with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} github_token: ${{ secrets.GITHUB_TOKEN }} claude_args: | --model opus --max-turns 20 --allowedTools "Bash(pnpm:*)" "Bash(node:*)" "Bash(git:*)" "Bash(gh:*)" "Bash(cat:*)" "Bash(rg:*)" prompt: | You are the API reference copy-sync agent. Your sole job: detect drift between auto-generated API JSON and hand-written MDX prose in reference pages. Trigger context: - Event: ${{ github.event_name }} - PR Number: #${{ needs.analyze.outputs.pr_number }} - PR Title: ${{ needs.analyze.outputs.pr_title }} - PR URL: ${{ needs.analyze.outputs.pr_url }} - PR Body: ${{ needs.analyze.outputs.pr_body }} Load context before acting: - Read `CLAUDE.md` and `.claude/skills/README.md`. - Load and follow these skills in order: 1. `.claude/skills/api-reference/SKILL.md` 2. `.claude/skills/docs/SKILL.md` 3. `.claude/skills/commit-pr/SKILL.md` Step 1 — Identify impacted components: - Use `gh pr diff ${{ needs.analyze.outputs.pr_number }} --name-only` (or `git log` for workflow_dispatch) to find which components/utils changed. - Focus only on those components for the rest of the analysis. Step 2 — Build API JSON: - Run `pnpm install` and `pnpm -C site api-docs`. - The generated JSON files are the source of truth — they are always current. Step 3 — Compare JSON against MDX: - For each impacted component, compare the generated API JSON with the hand-written MDX in `site/src/content/docs/reference/`. - "Drift" means the MDX prose doesn't match the JSON. Look for: - Missing or removed props/state fields in prose sections. - Outdated descriptions that contradict JSON. - Missing data attributes in Styling sections. - Stale type signatures in prose. - Missing sidebar entry in `site/src/docs.config.ts`. - For new components with no MDX yet: open an issue noting scaffold is needed, don't attempt it. Step 4 — Create/update issues and PRs: - Create/update one canonical issue per component using `.github/templates/docs/reference-copy-issue.md`. - Issue title: `docs(site): {component_name} api reference update`. - Keep at most one active canonical issue per component. - If a canonical issue exists, update it and append new trigger references. - Fully render template fields; no placeholders. - If drift is clear and actionable, create or update one canonical PR per component. - PR title: `docs(site): {component_name} api reference update`. - PR body must use `.github/templates/api-reference-update-pr.md`. - Do not open a PR when analysis is uncertain — use `triage` label instead. Labels: - Required: `docs`, `api`, `site`, `docs:reference-copy`. - Add `components` for component pages. - Add `triage` when uncertain. Scope restrictions: - ONLY check `site/src/content/docs/reference/` — no demos, no concept pages. - Do NOT check demo code (separate job handles that). - Do NOT check concept/how-to pages (separate job handles that). # ─── Job 2: Demo code drift (Claude Sonnet) ───────────────────────── docs-demos: needs: analyze if: needs.analyze.outputs.has_changes == 'true' runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v5 with: fetch-depth: 0 - name: Check demo code drift uses: anthropics/claude-code-action@v1 with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} github_token: ${{ secrets.GITHUB_TOKEN }} claude_args: | --model sonnet --max-turns 14 --allowedTools "Bash(git:*)" "Bash(gh:*)" "Bash(cat:*)" "Bash(rg:*)" prompt: | You are the API reference demo-sync agent. Your sole job: check if demo code uses APIs that changed in a recent PR. Trigger context: - Event: ${{ github.event_name }} - PR Number: #${{ needs.analyze.outputs.pr_number }} - PR Title: ${{ needs.analyze.outputs.pr_title }} - PR URL: ${{ needs.analyze.outputs.pr_url }} Load context before acting: - Read `CLAUDE.md`. Step 1 — Identify impacted components: - Use `gh pr diff ${{ needs.analyze.outputs.pr_number }} --name-only` (or `git log` for workflow_dispatch) to find which components changed. - Then use `gh pr diff ${{ needs.analyze.outputs.pr_number }}` to see what specifically changed in their APIs (renamed props, removed options, changed signatures, new data attributes). Step 2 — Check demos: - For each impacted component, read all demo files in `site/src/components/docs/demos/{component}/`. - Check both `html/css/` and `react/css/` variants. - Look for: renamed props, removed options, changed signatures, deprecated patterns, new data attributes demos should show. - Skip components with no demo directory. Step 3 — Create/update issues: - Create issues ONLY (no PRs — demo fixes need human judgment). - Use `.github/templates/docs/demos-issue.md`. - Issue title: `docs(site): {component_name} demo code update`. - Keep at most one active canonical issue per component. - If a canonical issue exists, update it and append new trigger references. - Fully render template fields; no placeholders. - Do NOT create an issue if no drift is found. Labels (always all of these): - `docs`, `api`, `site`, `docs:demos`, `components`, `triage`. Scope restrictions: - ONLY check `site/src/components/docs/demos/` — no MDX prose, no concept pages. - Do NOT check reference page content (separate job handles that). - Do NOT check concept/how-to pages (separate job handles that). # ─── Job 3: Guide references drift (Claude Sonnet) ────────────────── docs-guide-references: needs: analyze if: needs.analyze.outputs.has_changes == 'true' runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v5 with: fetch-depth: 0 - name: Check cross-site references uses: anthropics/claude-code-action@v1 with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} github_token: ${{ secrets.GITHUB_TOKEN }} claude_args: | --model sonnet --max-turns 14 --allowedTools "Bash(git:*)" "Bash(gh:*)" "Bash(cat:*)" "Bash(rg:*)" prompt: | You are the cross-site reference sync agent. Your sole job: find mentions of changed APIs in docs OUTSIDE `/reference/` that may be stale. Trigger context: - Event: ${{ github.event_name }} - PR Number: #${{ needs.analyze.outputs.pr_number }} - PR Title: ${{ needs.analyze.outputs.pr_title }} - PR URL: ${{ needs.analyze.outputs.pr_url }} Load context before acting: - Read `CLAUDE.md`. Step 1 — Identify changed APIs: - Use `gh pr diff ${{ needs.analyze.outputs.pr_number }} --name-only` (or `git log` for workflow_dispatch) to find which components/utils changed. - Then use `gh pr diff ${{ needs.analyze.outputs.pr_number }}` to identify the specific API changes (renamed props, removed exports, changed signatures, etc.). - Build a list of API names to search for (PascalCase, camelCase, kebab-case, `media-{name}` variants). Step 2 — Grep for references: - Run targeted `rg` commands to find files mentioning changed APIs in: - `site/src/content/docs/concepts/` - `site/src/content/docs/how-to/` - `packages/*/README.md` - Search for all name variants (kebab, PascalCase, camelCase, `media-{name}` element names). - Do NOT search in `site/src/content/docs/reference/` (separate job handles that). - Do NOT search in `site/src/components/docs/demos/` (separate job handles that). Step 3 — Analyze hits: - For each file with grep hits, read the surrounding context and determine: - `high` confidence: definitely stale (references removed/renamed API by old name). - `medium` confidence: likely stale (references API whose behavior changed). - `low` confidence / false positive: grep hit in unrelated context. - Be conservative — include borderline cases for human review. - Discard clear false positives. Step 4 — Create/update triage issues: - Create issues ONLY (no PRs). - Use `.github/templates/docs/guide-references-issue.md`. - Issue title: `docs(site): cross-site api references update`. - Group related APIs into one issue when they affect the same files. - Keep at most one active canonical issue (search for existing open issues with `drift-type:cross-site-refs` in body). - If a canonical issue exists, update it and append new trigger references. - Fully render template fields; no placeholders. - Do NOT create an issue if no stale references are found. Labels (always all of these): - `docs`, `site`, `docs:guide-references`, `triage`. Scope restrictions: - ONLY check docs outside `/reference/` and package READMEs. - Do NOT check reference pages (separate job handles that). - Do NOT check demo files (separate job handles that).