name: API Reference Sync on: pull_request: types: [closed] permissions: actions: read contents: write pull-requests: write issues: write id-token: write concurrency: group: api-reference-sync-${{ github.event.pull_request.number || github.run_id }} cancel-in-progress: true jobs: # ─── Job 0: Build API JSON at HEAD~1 and HEAD, diff them ───────────── analyze: if: github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'main' runs-on: ubuntu-latest outputs: has_diff: ${{ steps.classify.outputs.has_diff }} has_new: ${{ steps.classify.outputs.has_new }} steps: - name: Checkout code uses: actions/checkout@v5 with: fetch-depth: 0 - uses: pnpm/action-setup@v4 - uses: actions/setup-node@v5 with: node-version-file: '.nvmrc' cache: pnpm - name: Collect PR metadata id: meta env: PR_TITLE: ${{ github.event.pull_request.title || 'manual run' }} run: | mkdir -p /tmp/api-sync jq -n \ --arg number "${{ github.event.pull_request.number || '' }}" \ --arg url "${{ github.event.pull_request.html_url || '' }}" \ --arg author "${{ github.event.pull_request.user.login || '' }}" \ --arg title "$PR_TITLE" \ '{number: $number, url: $url, author: $author, title: $title}' \ > /tmp/api-sync/pr-meta.json cat /tmp/api-sync/pr-meta.json - name: Build API JSON at HEAD run: | pnpm install --frozen-lockfile pnpm -C site api-docs mkdir -p /tmp/api-sync/after-components /tmp/api-sync/after-utils cp -r site/src/content/generated-component-reference/* /tmp/api-sync/after-components/ 2>/dev/null || true cp -r site/src/content/generated-util-reference/* /tmp/api-sync/after-utils/ 2>/dev/null || true - name: Build API JSON at HEAD~1 run: | git checkout HEAD~1 # If install or build fails (e.g. api-docs-builder itself changed), # treat everything as new by leaving before dirs empty mkdir -p /tmp/api-sync/before-components /tmp/api-sync/before-utils if pnpm install --frozen-lockfile && pnpm -C site api-docs; then cp -r site/src/content/generated-component-reference/* /tmp/api-sync/before-components/ 2>/dev/null || true cp -r site/src/content/generated-util-reference/* /tmp/api-sync/before-utils/ 2>/dev/null || true else echo "::warning::HEAD~1 build failed — treating all components/utils as new" fi git checkout - - name: Classify changes id: classify run: | # Unified diff of both JSON directories diff -ruN /tmp/api-sync/before-components /tmp/api-sync/after-components > /tmp/api-sync/diff.patch || true diff -ruN /tmp/api-sync/before-utils /tmp/api-sync/after-utils >> /tmp/api-sync/diff.patch || true # New: files in after but not in before comm -23 \ <(ls /tmp/api-sync/after-components/ 2>/dev/null | sort) \ <(ls /tmp/api-sync/before-components/ 2>/dev/null | sort) \ > /tmp/api-sync/new-components.txt comm -23 \ <(ls /tmp/api-sync/after-utils/ 2>/dev/null | sort) \ <(ls /tmp/api-sync/before-utils/ 2>/dev/null | sort) \ > /tmp/api-sync/new-utils.txt # Changed: files in both, but content differs comm -12 \ <(ls /tmp/api-sync/after-components/ 2>/dev/null | sort) \ <(ls /tmp/api-sync/before-components/ 2>/dev/null | sort) \ | while read -r f; do if ! diff -q "/tmp/api-sync/before-components/$f" "/tmp/api-sync/after-components/$f" >/dev/null 2>&1; then echo "$f" fi done > /tmp/api-sync/changed-components.txt comm -12 \ <(ls /tmp/api-sync/after-utils/ 2>/dev/null | sort) \ <(ls /tmp/api-sync/before-utils/ 2>/dev/null | sort) \ | while read -r f; do if ! diff -q "/tmp/api-sync/before-utils/$f" "/tmp/api-sync/after-utils/$f" >/dev/null 2>&1; then echo "$f" fi done > /tmp/api-sync/changed-utils.txt # Log results echo "=== New components ===" && cat /tmp/api-sync/new-components.txt echo "=== New utils ===" && cat /tmp/api-sync/new-utils.txt echo "=== Changed components ===" && cat /tmp/api-sync/changed-components.txt echo "=== Changed utils ===" && cat /tmp/api-sync/changed-utils.txt echo "=== Diff ===" && head -100 /tmp/api-sync/diff.patch # Set outputs if [ -s /tmp/api-sync/diff.patch ]; then echo "has_diff=true" >> "$GITHUB_OUTPUT" else echo "has_diff=false" >> "$GITHUB_OUTPUT" fi if [ -s /tmp/api-sync/new-components.txt ] || [ -s /tmp/api-sync/new-utils.txt ]; then echo "has_new=true" >> "$GITHUB_OUTPUT" else echo "has_new=false" >> "$GITHUB_OUTPUT" fi - name: Upload artifact if: steps.classify.outputs.has_diff == 'true' uses: actions/upload-artifact@v4 with: name: api-diff path: /tmp/api-sync/ retention-days: 1 # ─── Job 1: Open issues for new components/utils without reference pages new-references: needs: analyze if: needs.analyze.outputs.has_new == 'true' runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v5 - name: Download artifact uses: actions/download-artifact@v4 with: name: api-diff path: /tmp/api-sync - name: Open issues for missing reference pages env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | set -euo pipefail PR_NUMBER=$(jq -r .number /tmp/api-sync/pr-meta.json) PR_URL=$(jq -r .url /tmp/api-sync/pr-meta.json) PR_AUTHOR=$(jq -r .author /tmp/api-sync/pr-meta.json) REF_DIR="site/src/content/docs/reference" # --- New components --- if [ -s /tmp/api-sync/new-components.txt ]; then while read -r filename; do JSON_PATH="/tmp/api-sync/after-components/$filename" NAME=$(jq -r .name "$JSON_PATH") SLUG="${filename%.json}" # Check if a reference page already uses this component if grep -rq "\` - [ ] Add sidebar entry in \`site/src/docs.config.ts\` - [ ] Verify build: \`pnpm -C site build\` ### Context - Triggering PR: $PR_URL - Generated JSON: \`site/src/content/generated-component-reference/$filename\` > Use the \`/api-reference $SLUG\` skill to scaffold this page. EOF )" gh issue create \ --title "docs(reference): add $NAME API reference page" \ --body "$BODY" \ --label "docs,docs:reference,site,components" \ $ASSIGNEE_FLAG echo "Created issue for component $NAME" done < /tmp/api-sync/new-components.txt fi # --- New utils --- if [ -s /tmp/api-sync/new-utils.txt ]; then while read -r filename; do JSON_PATH="/tmp/api-sync/after-utils/$filename" NAME=$(jq -r .name "$JSON_PATH") SLUG="${filename%.json}" # Check if a reference page already uses this util if grep -rq "\` - [ ] Add sidebar entry in \`site/src/docs.config.ts\` - [ ] Verify build: \`pnpm -C site build\` ### Context - Triggering PR: $PR_URL - Generated JSON: \`site/src/content/generated-util-reference/$filename\` > Use the \`/api-reference $SLUG\` skill to scaffold this page. EOF )" gh issue create \ --title "docs(reference): add $NAME API reference page" \ --body "$BODY" \ --label "docs,docs:reference,site" \ $ASSIGNEE_FLAG echo "Created issue for util $NAME" done < /tmp/api-sync/new-utils.txt fi # ─── Job 2: Detect stale docs (Claude Opus) ───────────────────────── stale-docs: needs: analyze if: needs.analyze.outputs.has_diff == 'true' runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v5 - name: Download artifact uses: actions/download-artifact@v4 with: name: api-diff path: /tmp/api-sync - name: Detect stale docs 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 25 --allowedTools "Read" "Glob" "Grep" "Bash(gh:*)" "Bash(cat:*)" "Bash(jq:*)" "Bash(diff:*)" "Bash(ls:*)" "Bash(head:*)" "Bash(tail:*)" "Bash(wc:*)" "Bash(sort:*)" prompt: | You are the stale-docs detection agent. Your job: given a pre-computed API diff, find documentation that may need updating and open ONE GitHub issue summarizing all findings. ## Inputs The following files are at /tmp/api-sync/: - `diff.patch` — unified diff of API JSON between HEAD~1 and HEAD - `changed-components.txt` — filenames of changed component JSONs (one per line, may be empty) - `changed-utils.txt` — filenames of changed util JSONs (one per line, may be empty) - `after-components/` — current component JSON files - `after-utils/` — current util JSON files - `pr-meta.json` — `{number, url, author, title}` of the triggering PR Important: Focus on items in `changed-components.txt` and `changed-utils.txt`. Skip any components/utils listed in `new-components.txt` or `new-utils.txt` — a separate job already opens issues for those. ## Process Step 1: Read `pr-meta.json` and `diff.patch`. Understand what API surfaces changed and how. Step 2: For each changed component/util, read the "after" JSON to understand the current API shape. Step 3: Search for documentation that references these APIs. Check ALL of: - `site/src/content/docs/reference/` — MDX reference pages (look for stale prop descriptions, missing new props/state/data-attributes, outdated type signatures) - `site/src/content/docs/concepts/` — concept pages mentioning these APIs - `site/src/content/docs/how-to/` — how-to guides mentioning these APIs - `site/src/components/docs/demos/` — demo code using changed props/state/signatures - `packages/*/README.md` — package READMEs mentioning these APIs Use Grep and Glob to search efficiently. Look for: - The component/util name (PascalCase, camelCase) - Specific prop/state/parameter names that were added, removed, or changed in the diff - HTML element names from `platforms.html.tagName` in the JSON (e.g. `media-play-button`) - Data attribute names that changed Step 4: For each file with hits, read the relevant section and assess: - `high` — definitely stale (references a removed/renamed prop by old name, shows old signature) - `medium` — likely stale (mentions API whose behavior/type changed) - `low` — possibly stale (mentions the component but may not be affected) Discard clear false positives. Include borderline cases for human review. Step 5: If you found ANY stale references, open exactly ONE issue using `gh issue create`. If you found NOTHING stale, do NOT create an issue — just output "No stale docs detected." ## Issue format Read `pr-meta.json` to get PR_NUMBER, PR_URL, PR_AUTHOR, and PR_TITLE. Assign the issue to the PR author: `--assignee $PR_AUTHOR` (skip if author is empty). Title: `docs(site): stale docs from #$PR_NUMBER` Labels: `docs,docs:reference,site,triage` Body (use this structure exactly): ``` ## Summary API changes in #{PR_NUMBER} ({PR_TITLE}) may have made the following documentation stale. ## Triggering PR {PR_URL} ## API Changes {Brief summary of what changed in the API — new props, removed props, type changes, etc.} ## Stale Documentation Found ### High Confidence | File | Line(s) | Issue | API Change | |---|---|---|---| {rows or "None found"} ### Medium Confidence | File | Line(s) | Issue | API Change | |---|---|---|---| {rows or "None found"} ### Low Confidence | File | Line(s) | Issue | API Change | |---|---|---|---| {rows or "None found"} ## Recommended Actions {Bulleted list of specific things to fix} ``` ## Rules - Open at most ONE issue per run. Group everything into one issue. - Do NOT create or modify any files. Do NOT create PRs. Issues only. - Do NOT open an issue if there is nothing stale. - Before creating an issue, check for existing open issues that have BOTH `` and `` in their body. If one exists, skip creation to avoid duplicates. - Be thorough but efficient — use Grep to find files, then Read only relevant sections.