From d9d5aa3d36bb0b299954ab592eeda1127f52adee Mon Sep 17 00:00:00 2001 From: rgarcia <72655+rgarcia@users.noreply.github.com> Date: Thu, 29 Oct 2026 21:14:35 +0000 Subject: [PATCH 1/2] Open an evergreen docs PR after each SDK publish --- .github/prompts/update-docs.md | 80 ++++ .../scripts/docs-update-disallowed-paths.sh | 12 + .github/workflows/update-docs.yml | 378 ++++++++++++++++++ 3 files changed, 470 insertions(+) create mode 100644 .github/prompts/update-docs.md create mode 100755 .github/scripts/docs-update-disallowed-paths.sh create mode 100644 .github/workflows/update-docs.yml diff --git a/.github/prompts/update-docs.md b/.github/prompts/update-docs.md new file mode 100644 index 00000000..0bafb96e --- /dev/null +++ b/.github/prompts/update-docs.md @@ -0,0 +1,80 @@ +# Update docs for the Node SDK + +You are working in the `kernel/docs` checkout. Read its AGENTS.md and +`.docs/code-example-guide.md` first and follow them for every change. +The published Node SDK source for the `SDK_VERSION` environment variable is in `../sdk`. +The Python and Go SDKs generated from the same API spec are in `../python-sdk` and +`../go-sdk`, checked out at the matching release tag when one exists. + +## Audit + +1. Read `$RUNNER_TEMP/sdk-diff.patch`. It covers `CHANGELOG.md`, `api.md`, and + `src/resources` in the Node SDK since the last release the docs were updated for. + Enumerate user-facing changes: new resources and methods, new or changed + parameters and response fields, new enum values, deprecations, and removals. +2. Read `$RUNNER_TEMP/openapi-diff.patch` and the matching parts of + `$RUNNER_TEMP/kernel-api/packages/api/openapi.yaml`. The SDKs are generated from + this spec, so its summaries and descriptions are the source of truth for what the + API does: limits, defaults, units, accepted values, and caveats. +3. Find every docs page that covers the affected features. Search the `.mdx` files for + the SDK method names, parameter names, and related concepts. Check `docs.json` for + navigation. + +## Update + +1. Update existing pages whose content the release makes wrong or incomplete: changed + signatures, renamed or removed parameters, new options on a documented feature, + new enum values in documented lists, and deprecations. +2. Document new user-facing capabilities. Prefer adding a section to the page that + already covers the feature. Create a new page only for a capability no existing page + covers, and add it to `docs.json` navigation next to related pages. +3. Every code example must call methods and parameters that exist in the checked-out + SDKs. Verify TypeScript against `../sdk/api.md` and `../sdk/src/resources`, Python + against `../python-sdk/api.md` and `../python-sdk/src/kernel/{resources,types}`, and Go + against `../go-sdk/api.md` and the Go sources. Include TypeScript and Python + versions, and a Go version wherever the surrounding page already shows Go. If a + language's SDK does not have the method yet, leave that language out rather than + guessing. +4. Preserve meaning exactly: limits, defaults, units, accepted values, deprecations, + and caveats must match the spec. Do not invent behavior the spec does not state. + Plan or pricing availability that the spec does not state is out of scope. +5. Follow the style of the surrounding page. Change only text the release affects or + that the release shows to be wrong. Do not restyle, reorganize, or reword unrelated + content. + +Skip changes with no user-facing effect: internal or hosted-login callback endpoints, +SDK codegen and dependency churn, and type-only refactors. Not every release needs a +docs change. If none does, change nothing. + +The API reference pages are generated from the published spec, so do not hand-write +endpoint reference pages. The CLI and MCP server reference (`reference/cli*`, +`reference/mcp-server*`) and `changelog.mdx` are maintained separately; do not edit them. + +## Validate and report + +Run `mint broken-links` from the docs checkout and fix any broken links your changes +introduce. Run `jq empty docs.json` if you edited `docs.json`. Do not run `mint dev`. + +Read the complete diff against origin/main before finishing. Remove filler, unrelated +changes, and anything that does not follow AGENTS.md. + +Write `$RUNNER_TEMP/docs-update-report.md` with concise sections describing: + +- Pages updated or added, with the SDK methods, parameters, or spec changes each + change follows. +- SDK changes intentionally left undocumented and why. +- Any languages left out of examples because an SDK lacks the method. +- Any validation limitations. + +If no docs changes are needed, explicitly say that the audit of the SDK diff found +nothing user-facing to document. Never claim checks passed unless you ran them. +The report becomes part of a public PR: include no credentials, customer information, +private discussions, or internal URLs. + +Do not commit, push, open or merge a PR. The workflow validates the changes and +opens or updates the evergreen PR only after all checks pass. + +Only `docs.json` and `.mdx` files are published, excluding hidden directories, +`changelog.mdx`, and the CLI and MCP server reference; the workflow discards every +other change before validation. Do not add images. Keep scratch files, logs, and +command output outside the checkout, under `$RUNNER_TEMP`. diff --git a/.github/scripts/docs-update-disallowed-paths.sh b/.github/scripts/docs-update-disallowed-paths.sh new file mode 100755 index 00000000..9301421a --- /dev/null +++ b/.github/scripts/docs-update-disallowed-paths.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +# Reads NUL-delimited docs paths on stdin and prints, NUL-delimited, +# the ones the docs updater must not publish. +set -euo pipefail + +while IFS= read -r -d '' path; do + case "$path" in + .* | changelog.mdx | reference/cli.mdx | reference/cli/* | reference/mcp-server.mdx | reference/mcp-server/*) ;; + docs.json | *.mdx) continue ;; + esac + printf '%s\0' "$path" +done diff --git a/.github/workflows/update-docs.yml b/.github/workflows/update-docs.yml new file mode 100644 index 00000000..7fe17a4d --- /dev/null +++ b/.github/workflows/update-docs.yml @@ -0,0 +1,378 @@ +name: Update Docs + +on: + workflow_run: + workflows: [Publish NPM] + types: [completed] + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: update-docs + cancel-in-progress: false + +jobs: + sdk-version: + if: >- + github.repository == 'kernel/kernel-node-sdk' && + (github.event_name == 'workflow_dispatch' || + (github.event.workflow_run.conclusion == 'success' && + github.event.workflow_run.head_repository.full_name == github.repository)) + runs-on: ubuntu-latest + outputs: + version: ${{ steps.version.outputs.version }} + sha: ${{ steps.version.outputs.sha }} + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + ref: ${{ github.event.workflow_run.head_sha || github.sha }} + persist-credentials: false + + - name: Resolve published SDK version + id: version + run: | + version=$(jq -er 'select(.name == "@onkernel/sdk") | .version' package.json) + if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Skipping non-stable SDK version: $version" + exit 0 + fi + echo "version=$version" >> "$GITHUB_OUTPUT" + echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" + + generate: + needs: sdk-version + if: needs.sdk-version.outputs.version != '' + runs-on: ubuntu-latest + timeout-minutes: 55 + outputs: + changed: ${{ steps.snapshot.outputs.changed }} + main_sha: ${{ steps.prepare.outputs.main_sha }} + update_sha: ${{ steps.prepare.outputs.update_sha }} + rebased: ${{ steps.prepare.outputs.rebased }} + agent_base_tree: ${{ steps.prepare.outputs.agent_base_tree }} + env: + SDK_VERSION: ${{ needs.sdk-version.outputs.version }} + SDK_SHA: ${{ needs.sdk-version.outputs.sha }} + UPDATE_BRANCH: sdk-docs-update + steps: + - name: Checkout updater instructions + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + persist-credentials: false + + - name: Checkout published SDK + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + ref: ${{ env.SDK_SHA }} + path: sdk + fetch-depth: 0 + persist-credentials: false + + - name: Checkout Python SDK + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + repository: kernel/kernel-python-sdk + path: python-sdk + fetch-depth: 0 + persist-credentials: false + + - name: Checkout Go SDK + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + repository: kernel/kernel-go-sdk + path: go-sdk + fetch-depth: 0 + persist-credentials: false + + - name: Select matching Python and Go SDK releases + run: | + for sdk in python-sdk go-sdk; do + if git -C "$sdk" rev-parse --verify --quiet "refs/tags/v$SDK_VERSION" >/dev/null; then + git -C "$sdk" switch -q --detach "v$SDK_VERSION" + else + echo "::warning::$sdk has no v$SDK_VERSION tag yet; using its default branch." + fi + done + + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version: '22' + + - name: Checkout docs + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + repository: kernel/docs + path: docs + fetch-depth: 0 + persist-credentials: false + + - name: Prepare update branch and SDK diff + id: prepare + working-directory: docs + run: | + git config user.name 'kernel-internal[bot]' + git config user.email '260533166+kernel-internal[bot]@users.noreply.github.com' + echo "main_sha=$(git rev-parse origin/main)" >> "$GITHUB_OUTPUT" + if git show-ref --verify --quiet "refs/remotes/origin/$UPDATE_BRANCH"; then + echo "update_sha=$(git rev-parse "origin/$UPDATE_BRANCH")" >> "$GITHUB_OUTPUT" + git switch --track "origin/$UPDATE_BRANCH" + if ! git rebase origin/main; then + git rebase --abort + # Covered versions are read from history, so a bot-only branch can be + # regenerated from main. Human commits must be kept. + if git log --no-merges --format=%ae "origin/main..origin/$UPDATE_BRANCH" \ + | grep -qvxF '260533166+kernel-internal[bot]@users.noreply.github.com'; then + echo "::error::$UPDATE_BRANCH has human commits that conflict with main. Resolve the conflicts on that branch, then rerun this workflow." + exit 1 + fi + echo "::warning::$UPDATE_BRANCH conflicts with main; regenerating it from main." + git switch -C "$UPDATE_BRANCH" origin/main + else + echo "rebased=true" >> "$GITHUB_OUTPUT" + fi + else + git switch -c "$UPDATE_BRANCH" origin/main + fi + # The agent's changes are published on top of this head, so human + # commits on the update branch keep their content and authorship. + echo "agent_base=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" + echo "agent_base_tree=$(git rev-parse 'HEAD^{tree}')" >> "$GITHUB_OUTPUT" + + # Update commits and the squash-merged PR title record the SDK version + # the docs already cover. Without one, start from the previous release. + old_version=$(git log --format=%s HEAD \ + | sed -nE 's/^Docs: Update for Node SDK ([0-9]+\.[0-9]+\.[0-9]+)( \(#[0-9]+\))?$/\1/p' \ + | sort -V | tail -1) + if [[ -z "$old_version" ]]; then + old_version=$(git -C ../sdk tag --merged "$SDK_SHA^" --list 'v*' \ + | sed -nE 's/^v([0-9]+\.[0-9]+\.[0-9]+)$/\1/p' | sort -V | tail -1) + fi + if [[ -n "$old_version" ]] && + [[ "$(printf '%s\n' "$old_version" "$SDK_VERSION" | sort -V | tail -1)" == "$old_version" ]]; then + echo "Docs already cover SDK $old_version; skipping stale run." + exit 0 + fi + + if [[ -n "$old_version" ]] && git -C ../sdk rev-parse --verify "refs/tags/v$old_version" >/dev/null 2>&1; then + git -C ../sdk diff "refs/tags/v$old_version" "$SDK_SHA" -- CHANGELOG.md api.md src/resources > "$RUNNER_TEMP/sdk-diff.patch" + echo "old_date=$(TZ=UTC git -C ../sdk log -1 --date=format-local:%Y-%m-%dT%H:%M:%SZ --format=%cd "refs/tags/v$old_version")" >> "$GITHUB_OUTPUT" + else + printf 'Baseline unavailable; audit all of api.md and src/resources against the docs.\n' > "$RUNNER_TEMP/sdk-diff.patch" + fi + echo "new_date=$(TZ=UTC git -C ../sdk log -1 --date=format-local:%Y-%m-%dT%H:%M:%SZ --format=%cd "$SDK_SHA")" >> "$GITHUB_OUTPUT" + echo "update=true" >> "$GITHUB_OUTPUT" + + - name: Generate API read token + if: steps.prepare.outputs.update == 'true' + id: api-read-token + uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1 + with: + app-id: ${{ secrets.ADMIN_APP_ID }} + private-key: ${{ secrets.ADMIN_APP_PRIVATE_KEY }} + owner: kernel + repositories: kernel + permission-contents: read + + - name: Fetch API spec + if: steps.prepare.outputs.update == 'true' + env: + GH_TOKEN: ${{ steps.api-read-token.outputs.token }} + OLD_DATE: ${{ steps.prepare.outputs.old_date }} + NEW_DATE: ${{ steps.prepare.outputs.new_date }} + run: | + main_at() { + gh api -X GET repos/kernel/kernel/commits -f sha=main -f until="$1" -f per_page=1 --jq '.[0].sha // empty' + } + file_at() { + gh api -H 'Accept: application/vnd.github.raw' "repos/kernel/kernel/contents/$1?ref=$2" + } + spec="$RUNNER_TEMP/kernel-api/packages/api/openapi.yaml" + head=$(main_at "$NEW_DATE") + test -n "$head" + mkdir -p "$(dirname "$spec")" + file_at packages/api/openapi.yaml "$head" > "$spec" + base="" + if [[ -n "$OLD_DATE" ]]; then + base=$(main_at "$OLD_DATE") + fi + if [[ -n "$base" ]]; then + file_at packages/api/openapi.yaml "$base" > "$RUNNER_TEMP/openapi-base.yaml" + diff -u --label "openapi.yaml@$base" --label "openapi.yaml@$head" \ + "$RUNNER_TEMP/openapi-base.yaml" "$spec" \ + > "$RUNNER_TEMP/openapi-diff.patch" || [[ $? -eq 1 ]] + rm "$RUNNER_TEMP/openapi-base.yaml" + else + printf 'Spec baseline unavailable; compare all relevant descriptions in openapi.yaml.\n' > "$RUNNER_TEMP/openapi-diff.patch" + fi + gh api --method DELETE /installation/token + + - name: Install Pi and Mintlify CLI + if: steps.prepare.outputs.update == 'true' + env: + PI_TELEMETRY: '0' + run: | + # mint matches the version pinned by the docs repo's broken-links check. + npm install -g --ignore-scripts @earendil-works/pi-coding-agent mint@4.2.930 + pi --version + + - name: Update docs + if: steps.prepare.outputs.update == 'true' + working-directory: docs + env: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + PI_PREFERRED_MODEL: ${{ vars.PI_PREFERRED_MODEL }} + PI_SKIP_VERSION_CHECK: '1' + PI_TELEMETRY: '0' + run: | + : "${PI_PREFERRED_MODEL:?Set the PI_PREFERRED_MODEL organization variable}" + pi --print --no-session --approve \ + --model "$PI_PREFERRED_MODEL" --thinking max \ + @../.github/prompts/update-docs.md + + - name: Snapshot publishable changes + if: steps.prepare.outputs.update == 'true' + id: snapshot + working-directory: docs + env: + AGENT_BASE: ${{ steps.prepare.outputs.agent_base }} + run: | + git reset -q --soft "$AGENT_BASE" + git add -A + git diff --cached --name-only --no-renames -z \ + | bash ../.github/scripts/docs-update-disallowed-paths.sh > "$RUNNER_TEMP/excluded-paths" + if [[ -s "$RUNNER_TEMP/excluded-paths" ]]; then + echo "::warning::Discarding changes outside the docs updater allowlist:" + tr '\0' '\n' < "$RUNNER_TEMP/excluded-paths" + GIT_LITERAL_PATHSPECS=1 git reset -q --pathspec-from-file="$RUNNER_TEMP/excluded-paths" --pathspec-file-nul + fi + git diff --cached --binary --no-renames --no-ext-diff --no-textconv > "$RUNNER_TEMP/changes.patch" + git reset -q --hard + git clean -fdq + if [[ -s "$RUNNER_TEMP/changes.patch" ]]; then + git apply --index "$RUNNER_TEMP/changes.patch" + echo "changed=true" >> "$GITHUB_OUTPUT" + fi + + - name: Verify docs changes + if: steps.prepare.outputs.update == 'true' + working-directory: docs + run: | + test -s "$RUNNER_TEMP/docs-update-report.md" + jq empty docs.json + mint broken-links + git diff --cached --check + + - name: Upload validated changes + if: steps.snapshot.outputs.changed == 'true' + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: sdk-docs-update + path: | + ${{ runner.temp }}/changes.patch + ${{ runner.temp }}/docs-update-report.md + if-no-files-found: error + retention-days: 7 + + publish: + needs: [sdk-version, generate] + if: needs.generate.outputs.changed == 'true' + runs-on: ubuntu-latest + timeout-minutes: 10 + env: + SDK_VERSION: ${{ needs.sdk-version.outputs.version }} + MAIN_SHA: ${{ needs.generate.outputs.main_sha }} + UPDATE_SHA: ${{ needs.generate.outputs.update_sha }} + REBASED: ${{ needs.generate.outputs.rebased }} + AGENT_BASE_TREE: ${{ needs.generate.outputs.agent_base_tree }} + UPDATE_BRANCH: sdk-docs-update + steps: + - name: Checkout updater scripts + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + persist-credentials: false + + - name: Download validated changes + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: sdk-docs-update + path: ${{ runner.temp }}/sdk-docs-update + + - name: Checkout docs + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + repository: kernel/docs + path: docs + fetch-depth: 0 + persist-credentials: false + + - name: Apply validated changes + working-directory: docs + run: | + git config user.name 'kernel-internal[bot]' + git config user.email '260533166+kernel-internal[bot]@users.noreply.github.com' + if [[ "$REBASED" == true ]]; then + git switch -c "$UPDATE_BRANCH" "$UPDATE_SHA" + git rebase -q "$MAIN_SHA" + else + git switch -c "$UPDATE_BRANCH" "$MAIN_SHA" + fi + if [[ "$(git rev-parse 'HEAD^{tree}')" != "$AGENT_BASE_TREE" ]]; then + echo "::error::The rebuilt update branch differs from the one the changes were validated on." + exit 1 + fi + git apply --index "$RUNNER_TEMP/sdk-docs-update/changes.patch" + git diff --cached --name-only --no-renames -z \ + | bash ../.github/scripts/docs-update-disallowed-paths.sh > "$RUNNER_TEMP/disallowed-paths" + if [[ -s "$RUNNER_TEMP/disallowed-paths" ]]; then + echo "::error::Validated changes include paths outside the docs updater allowlist:" + tr '\0' '\n' < "$RUNNER_TEMP/disallowed-paths" + exit 1 + fi + if git diff --cached --raw --no-renames | awk '$2 !~ /^(100644|100755|000000)$/ { found = 1 } END { exit !found }'; then + echo "::error::Validated changes may only contain regular files." + exit 1 + fi + git commit -q -m "Docs: Update for Node SDK $SDK_VERSION" + + - name: Generate docs repository token + id: app-token + uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1 + with: + app-id: ${{ secrets.ADMIN_APP_ID }} + private-key: ${{ secrets.ADMIN_APP_PRIVATE_KEY }} + owner: kernel + repositories: docs + permission-contents: write + permission-pull-requests: write + + - name: Open or update docs PR + working-directory: docs + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + run: | + if git diff --quiet origin/main HEAD; then + echo "Docs are already up to date." + exit 0 + fi + gh auth setup-git + git push --force-with-lease="$UPDATE_BRANCH:$UPDATE_SHA" origin "$UPDATE_BRANCH" + + body="$RUNNER_TEMP/docs-update-pr.md" + { + printf '## Summary\n\n- Document `@onkernel/sdk` changes through `%s`.\n' "$SDK_VERSION" + printf '\n## Changes\n\n' + head -c 60000 "$RUNNER_TEMP/sdk-docs-update/docs-update-report.md" + printf '\n## Validation\n\n- `mint broken-links`\n- `docs.json` parses\n' + } > "$body" + pr=$(gh pr list --repo kernel/docs --head "$UPDATE_BRANCH" --base main \ + --state open --json number --jq '.[0].number // empty') + title="Docs: Update for Node SDK $SDK_VERSION" + if [[ -n "$pr" ]]; then + gh pr edit "$pr" --repo kernel/docs --title "$title" --body-file "$body" + else + gh pr create --repo kernel/docs --head "$UPDATE_BRANCH" --base main \ + --title "$title" --body-file "$body" + fi From a394d5088f72fa18a85c64660c34545b3b419172 Mon Sep 17 00:00:00 2001 From: rgarcia <72655+rgarcia@users.noreply.github.com> Date: Fri, 30 Oct 2026 14:38:55 +0000 Subject: [PATCH 2/2] Default the docs updater to no change and audit one release per run --- .github/prompts/update-docs.md | 141 +++++++++++------- .../scripts/docs-update-disallowed-paths.sh | 2 +- .github/workflows/update-docs.yml | 76 +++++++--- 3 files changed, 144 insertions(+), 75 deletions(-) diff --git a/.github/prompts/update-docs.md b/.github/prompts/update-docs.md index 0bafb96e..4f4d7efb 100644 --- a/.github/prompts/update-docs.md +++ b/.github/prompts/update-docs.md @@ -1,80 +1,111 @@ # Update docs for the Node SDK -You are working in the `kernel/docs` checkout. Read its AGENTS.md and +You are working in the `kernel/docs` checkout, on a branch that may already hold +docs updates from earlier SDK releases. Read its AGENTS.md and `.docs/code-example-guide.md` first and follow them for every change. -The published Node SDK source for the `SDK_VERSION` environment variable is in `../sdk`. -The Python and Go SDKs generated from the same API spec are in `../python-sdk` and -`../go-sdk`, checked out at the matching release tag when one exists. + +## Default to no change + +Most SDK releases need no docs change. People write feature docs, usually within a +day of a release, and the API reference is generated from the published spec, so new +endpoints, parameters, response fields, and enum values already appear there. Be +skeptical: change the docs only when existing guide text or a code example is now +wrong or misleading for someone using this release. Finishing with no changes is +the expected outcome for most runs. + +Change docs for: + +- Code examples that call a renamed, removed, or re-signatured method or parameter. +- Prose that states a limit, default, accepted value, or behavior the release changed. +- Guidance that recommends something the release deprecates or removes. +- A short addition to an existing section, only when leaving the new option out would + mislead a reader of that section, such as a list of accepted values that is + presented as complete. + +Do not: + +- Create new pages or new top-level sections. List undocumented capabilities in the + report as missing coverage instead. +- Document something already covered by a recent docs commit or an open docs PR. +- Restate the generated API reference in guides. +- Restyle, reorganize, or reword content the release does not affect. + +## Inputs + +- `$RUNNER_TEMP/sdk-diff.patch`: Node SDK changes for this release (`CHANGELOG.md`, + `api.md`, `src/client.ts`, `src/lib`, `src/resources`). The audit covers this diff. +- `../sdk`: the published Node SDK for the `SDK_VERSION` environment variable. +- `../python-sdk` and `../go-sdk`: the Python and Go SDKs, at the matching release tag + when one exists, otherwise their default branch. +- `$RUNNER_TEMP/kernel-api/packages/api/openapi.yaml` and `$RUNNER_TEMP/openapi-diff.patch`: + the Kernel API spec and its changes over the same window. +- `$RUNNER_TEMP/recent-docs-commits.txt`: docs commits on main since the previous + release, with the files they touched. +- `$RUNNER_TEMP/open-docs-prs.txt`: open docs PRs and the files they touch. +- `$RUNNER_TEMP/previous-report.md`, if present: the report for changes already on + this branch from earlier releases. + +The spec can be ahead of the released SDK. The SDK defines what exists in this +release: never document an operation, parameter, or field the checked-out SDKs do not +have. Use the spec only for the meaning of things the SDK has: limits, defaults, +units, accepted values, and caveats. Do not document operations marked `x-hidden` or +`x-stainless-skip` in the spec, even if an SDK exposes them. ## Audit -1. Read `$RUNNER_TEMP/sdk-diff.patch`. It covers `CHANGELOG.md`, `api.md`, and - `src/resources` in the Node SDK since the last release the docs were updated for. - Enumerate user-facing changes: new resources and methods, new or changed - parameters and response fields, new enum values, deprecations, and removals. -2. Read `$RUNNER_TEMP/openapi-diff.patch` and the matching parts of - `$RUNNER_TEMP/kernel-api/packages/api/openapi.yaml`. The SDKs are generated from - this spec, so its summaries and descriptions are the source of truth for what the - API does: limits, defaults, units, accepted values, and caveats. -3. Find every docs page that covers the affected features. Search the `.mdx` files for - the SDK method names, parameter names, and related concepts. Check `docs.json` for - navigation. +1. Enumerate the user-facing changes in the SDK diff: removed or renamed methods and + parameters, changed signatures, changed defaults or limits, deprecations, and new + capabilities. Skip codegen churn, dependency updates, type-only refactors, and + internal or hosted-login callback endpoints. +2. For each change, search the `.mdx` files for the method names, parameter names, and + related concepts, and decide whether any existing text is now wrong or misleading. +3. Check the recent commits and open PRs before editing a page they cover. ## Update -1. Update existing pages whose content the release makes wrong or incomplete: changed - signatures, renamed or removed parameters, new options on a documented feature, - new enum values in documented lists, and deprecations. -2. Document new user-facing capabilities. Prefer adding a section to the page that - already covers the feature. Create a new page only for a capability no existing page - covers, and add it to `docs.json` navigation next to related pages. -3. Every code example must call methods and parameters that exist in the checked-out - SDKs. Verify TypeScript against `../sdk/api.md` and `../sdk/src/resources`, Python - against `../python-sdk/api.md` and `../python-sdk/src/kernel/{resources,types}`, and Go - against `../go-sdk/api.md` and the Go sources. Include TypeScript and Python - versions, and a Go version wherever the surrounding page already shows Go. If a - language's SDK does not have the method yet, leave that language out rather than - guessing. -4. Preserve meaning exactly: limits, defaults, units, accepted values, deprecations, - and caveats must match the spec. Do not invent behavior the spec does not state. - Plan or pricing availability that the spec does not state is out of scope. -5. Follow the style of the surrounding page. Change only text the release affects or - that the release shows to be wrong. Do not restyle, reorganize, or reword unrelated - content. - -Skip changes with no user-facing effect: internal or hosted-login callback endpoints, -SDK codegen and dependency churn, and type-only refactors. Not every release needs a -docs change. If none does, change nothing. - -The API reference pages are generated from the published spec, so do not hand-write -endpoint reference pages. The CLI and MCP server reference (`reference/cli*`, -`reference/mcp-server*`) and `changelog.mdx` are maintained separately; do not edit them. +1. Fix only what the audit found. Follow the style of the surrounding page. +2. Every code example you touch must call methods and parameters that exist in the + checked-out SDKs. Verify TypeScript against `../sdk/api.md` and `../sdk/src`, Python + against `../python-sdk/api.md` and `../python-sdk/src/kernel/{resources,types}`, and + Go against `../go-sdk/api.md` and the Go sources. Keep every language the example + already shows. If a language's SDK does not have the method yet, leave the existing + example in that language unchanged and say so in the report. +3. Preserve meaning exactly. Do not invent behavior the spec does not state. Plan or + pricing availability is out of scope. + +The CLI and MCP server reference (`reference/cli*`, `reference/mcp-server*`) and +`changelog.mdx` are maintained separately; do not edit them. ## Validate and report -Run `mint broken-links` from the docs checkout and fix any broken links your changes -introduce. Run `jq empty docs.json` if you edited `docs.json`. Do not run `mint dev`. +If you changed anything, run `mint broken-links` from the docs checkout and fix any +broken links your changes introduce, and run `jq empty docs.json` if you edited it. +Do not run `mint dev`. -Read the complete diff against origin/main before finishing. Remove filler, unrelated -changes, and anything that does not follow AGENTS.md. +Read the complete diff against origin/main before finishing. It includes changes from +earlier releases still on this branch. Remove filler, unrelated changes, and anything +that does not follow AGENTS.md. -Write `$RUNNER_TEMP/docs-update-report.md` with concise sections describing: +Write `$RUNNER_TEMP/docs-update-report.md`. It replaces the PR description, so it +must describe every change in the diff against origin/main, not only this run's. +Carry forward entries from `$RUNNER_TEMP/previous-report.md` that still match the +diff. Use concise sections: -- Pages updated or added, with the SDK methods, parameters, or spec changes each +- Pages changed, with the SDK release and the method, parameter, or spec change each change follows. -- SDK changes intentionally left undocumented and why. -- Any languages left out of examples because an SDK lacks the method. +- Missing coverage: new capabilities with no guide coverage, for a person to decide on. +- Languages left unchanged because an SDK lacks the method. - Any validation limitations. -If no docs changes are needed, explicitly say that the audit of the SDK diff found -nothing user-facing to document. Never claim checks passed unless you ran them. -The report becomes part of a public PR: include no credentials, customer information, -private discussions, or internal URLs. +If this release needed no docs change, say so in one line and list any missing +coverage. Never claim checks passed unless you ran them. The report becomes part of a +public PR: include no credentials, customer information, private discussions, or +internal URLs. Do not commit, push, open or merge a PR. The workflow validates the changes and opens or updates the evergreen PR only after all checks pass. -Only `docs.json` and `.mdx` files are published, excluding hidden directories, +Only `docs.json` and `.mdx` files are published, excluding hidden paths, `changelog.mdx`, and the CLI and MCP server reference; the workflow discards every other change before validation. Do not add images. Keep scratch files, logs, and command output outside the checkout, under `$RUNNER_TEMP`. diff --git a/.github/scripts/docs-update-disallowed-paths.sh b/.github/scripts/docs-update-disallowed-paths.sh index 9301421a..76ab9e47 100755 --- a/.github/scripts/docs-update-disallowed-paths.sh +++ b/.github/scripts/docs-update-disallowed-paths.sh @@ -5,7 +5,7 @@ set -euo pipefail while IFS= read -r -d '' path; do case "$path" in - .* | changelog.mdx | reference/cli.mdx | reference/cli/* | reference/mcp-server.mdx | reference/mcp-server/*) ;; + .* | */.* | changelog.mdx | reference/cli.mdx | reference/cli/* | reference/mcp-server.mdx | reference/mcp-server/*) ;; docs.json | *.mdx) continue ;; esac printf '%s\0' "$path" diff --git a/.github/workflows/update-docs.yml b/.github/workflows/update-docs.yml index 7fe17a4d..7e44f0b4 100644 --- a/.github/workflows/update-docs.yml +++ b/.github/workflows/update-docs.yml @@ -5,6 +5,11 @@ on: workflows: [Publish NPM] types: [completed] workflow_dispatch: + inputs: + since_version: + description: 'Audit SDK changes after this release (for example 0.130.0) instead of the computed baseline' + required: false + type: string permissions: contents: read @@ -38,8 +43,10 @@ jobs: echo "Skipping non-stable SDK version: $version" exit 0 fi + # Manual runs start from main, which can be ahead of the release. + git fetch -q --depth=1 origin "refs/tags/v$version:refs/tags/v$version" echo "version=$version" >> "$GITHUB_OUTPUT" - echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" + echo "sha=$(git rev-parse "refs/tags/v$version^{commit}")" >> "$GITHUB_OUTPUT" generate: needs: sdk-version @@ -55,6 +62,7 @@ jobs: env: SDK_VERSION: ${{ needs.sdk-version.outputs.version }} SDK_SHA: ${{ needs.sdk-version.outputs.sha }} + SINCE_VERSION: ${{ inputs.since_version }} UPDATE_BRANCH: sdk-docs-update steps: - name: Checkout updater instructions @@ -120,8 +128,8 @@ jobs: git switch --track "origin/$UPDATE_BRANCH" if ! git rebase origin/main; then git rebase --abort - # Covered versions are read from history, so a bot-only branch can be - # regenerated from main. Human commits must be kept. + # A bot-only branch can be regenerated from main by re-auditing every + # release since main's last docs update. Human commits must be kept. if git log --no-merges --format=%ae "origin/main..origin/$UPDATE_BRANCH" \ | grep -qvxF '260533166+kernel-internal[bot]@users.noreply.github.com'; then echo "::error::$UPDATE_BRANCH has human commits that conflict with main. Resolve the conflicts on that branch, then rerun this workflow." @@ -129,6 +137,7 @@ jobs: fi echo "::warning::$UPDATE_BRANCH conflicts with main; regenerating it from main." git switch -C "$UPDATE_BRANCH" origin/main + regenerated=true else echo "rebased=true" >> "$GITHUB_OUTPUT" fi @@ -140,30 +149,57 @@ jobs: echo "agent_base=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" echo "agent_base_tree=$(git rev-parse 'HEAD^{tree}')" >> "$GITHUB_OUTPUT" - # Update commits and the squash-merged PR title record the SDK version - # the docs already cover. Without one, start from the previous release. - old_version=$(git log --format=%s HEAD \ + # Bot commits record each audited release, so a run older than one + # already on the branch or main is stale. + covered=$(git log --format=%s HEAD \ | sed -nE 's/^Docs: Update for Node SDK ([0-9]+\.[0-9]+\.[0-9]+)( \(#[0-9]+\))?$/\1/p' \ | sort -V | tail -1) + if [[ -n "$covered" ]] && + [[ "$(printf '%s\n' "$covered" "$SDK_VERSION" | sort -V | tail -1)" == "$covered" ]]; then + echo "Docs already cover SDK $covered; skipping stale run." + exit 0 + fi + + # Each run audits one release; earlier releases were audited by + # earlier runs, whether or not they changed anything. + old_version=$SINCE_VERSION + if [[ -z "$old_version" && "${regenerated:-}" == true ]]; then + old_version=$covered + fi if [[ -z "$old_version" ]]; then old_version=$(git -C ../sdk tag --merged "$SDK_SHA^" --list 'v*' \ | sed -nE 's/^v([0-9]+\.[0-9]+\.[0-9]+)$/\1/p' | sort -V | tail -1) fi - if [[ -n "$old_version" ]] && - [[ "$(printf '%s\n' "$old_version" "$SDK_VERSION" | sort -V | tail -1)" == "$old_version" ]]; then - echo "Docs already cover SDK $old_version; skipping stale run." - exit 0 - fi + git -C ../sdk rev-parse --verify --quiet "refs/tags/v$old_version" >/dev/null + git -C ../sdk diff "refs/tags/v$old_version" "$SDK_SHA" \ + -- CHANGELOG.md api.md src/client.ts src/lib src/resources > "$RUNNER_TEMP/sdk-diff.patch" + old_date=$(TZ=UTC git -C ../sdk log -1 --date=format-local:%Y-%m-%dT%H:%M:%SZ --format=%cd "refs/tags/v$old_version") + { + echo "old_date=$old_date" + echo "new_date=$(TZ=UTC git -C ../sdk log -1 --date=format-local:%Y-%m-%dT%H:%M:%SZ --format=%cd "$SDK_SHA")" + } >> "$GITHUB_OUTPUT" - if [[ -n "$old_version" ]] && git -C ../sdk rev-parse --verify "refs/tags/v$old_version" >/dev/null 2>&1; then - git -C ../sdk diff "refs/tags/v$old_version" "$SDK_SHA" -- CHANGELOG.md api.md src/resources > "$RUNNER_TEMP/sdk-diff.patch" - echo "old_date=$(TZ=UTC git -C ../sdk log -1 --date=format-local:%Y-%m-%dT%H:%M:%SZ --format=%cd "refs/tags/v$old_version")" >> "$GITHUB_OUTPUT" - else - printf 'Baseline unavailable; audit all of api.md and src/resources against the docs.\n' > "$RUNNER_TEMP/sdk-diff.patch" - fi - echo "new_date=$(TZ=UTC git -C ../sdk log -1 --date=format-local:%Y-%m-%dT%H:%M:%SZ --format=%cd "$SDK_SHA")" >> "$GITHUB_OUTPUT" + # Docs people wrote since the baseline, so the agent does not duplicate them. + git log --since="$old_date" --name-only --format='%h %s' origin/main -- '*.mdx' docs.json \ + > "$RUNNER_TEMP/recent-docs-commits.txt" echo "update=true" >> "$GITHUB_OUTPUT" + - name: Collect open docs PRs + if: steps.prepare.outputs.update == 'true' + env: + GH_TOKEN: ${{ github.token }} + REBASED: ${{ steps.prepare.outputs.rebased }} + run: | + gh pr list --repo kernel/docs --state open --limit 100 \ + --json number,title,headRefName,files \ + --jq '.[] | select(.headRefName != env.UPDATE_BRANCH) | "#\(.number) \(.title)\n" + ([.files[].path | " " + .] | join("\n"))' \ + > "$RUNNER_TEMP/open-docs-prs.txt" + # The new report must still describe the changes already on the branch. + if [[ "$REBASED" == true ]]; then + gh pr list --repo kernel/docs --head "$UPDATE_BRANCH" --base main --state open \ + --json body --jq '.[0].body // empty' > "$RUNNER_TEMP/previous-report.md" + fi + - name: Generate API read token if: steps.prepare.outputs.update == 'true' id: api-read-token @@ -261,6 +297,8 @@ jobs: working-directory: docs run: | test -s "$RUNNER_TEMP/docs-update-report.md" + # No-op runs open no PR, so their missing-coverage notes live here. + cat "$RUNNER_TEMP/docs-update-report.md" >> "$GITHUB_STEP_SUMMARY" jq empty docs.json mint broken-links git diff --cached --check @@ -365,7 +403,7 @@ jobs: printf '## Summary\n\n- Document `@onkernel/sdk` changes through `%s`.\n' "$SDK_VERSION" printf '\n## Changes\n\n' head -c 60000 "$RUNNER_TEMP/sdk-docs-update/docs-update-report.md" - printf '\n## Validation\n\n- `mint broken-links`\n- `docs.json` parses\n' + printf '\n## Validation\n\n- `mint broken-links`\n- `docs.json` parses\n- `git diff --check`\n' } > "$body" pr=$(gh pr list --repo kernel/docs --head "$UPDATE_BRANCH" --base main \ --state open --json number --jq '.[0].number // empty')