ci: throttle docs agent workflow

This commit is contained in:
Peter Steinberger
2026-04-24 00:27:01 +01:00
parent 1e24bee879
commit f7c4d7a5f0
3 changed files with 85 additions and 19 deletions

View File

@@ -23,7 +23,7 @@ Allowed paths:
Required workflow: Required workflow:
1. Run `pnpm docs:list` if available and read relevant docs based on `read_when` hints. 1. Run `pnpm docs:list` if available and read relevant docs based on `read_when` hints.
2. Inspect the triggering event via `$GITHUB_EVENT_PATH`, then review the relevant commit range and changed files. 2. Inspect the triggering event via `$GITHUB_EVENT_PATH`, then review `$DOCS_AGENT_BASE_SHA..$DOCS_AGENT_HEAD_SHA` and its changed files. If either env var is missing, fall back to the event payload.
3. Update stale existing documentation, if needed. 3. Update stale existing documentation, if needed.
4. Run `pnpm check:docs` if dependencies are available. 4. Run `pnpm check:docs` if dependencies are available.
5. Leave the worktree clean if no docs need changes. 5. Leave the worktree clean if no docs need changes.

View File

@@ -1,16 +1,15 @@
name: Docs Agent name: Docs Agent
on: on:
workflow_run: # zizmor: ignore[dangerous-triggers] main-only docs repair after trusted CI; job gates repository, event, branch, actor, conclusion, and exact current main SHA before using write token workflow_run: # zizmor: ignore[dangerous-triggers] main-only docs repair after trusted CI; job gates repository, event, branch, actor, conclusion, exact current main SHA, and hourly cadence before using write token
workflows: workflows:
- CI - CI
types: types:
- completed - completed
schedule:
- cron: "17 5 * * *"
workflow_dispatch: workflow_dispatch:
permissions: permissions:
actions: read
contents: write contents: write
concurrency: concurrency:
@@ -41,17 +40,24 @@ jobs:
persist-credentials: false persist-credentials: false
submodules: false submodules: false
- name: Skip superseded workflow runs - name: Gate trusted main activity and hourly cadence
id: superseded id: gate
env: env:
EVENT_NAME: ${{ github.event_name }} EVENT_NAME: ${{ github.event_name }}
GH_TOKEN: ${{ github.token }}
WORKFLOW_HEAD_SHA: ${{ github.event.workflow_run.head_sha }} WORKFLOW_HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
run: | run: |
set -euo pipefail set -euo pipefail
if [ "$EVENT_NAME" != "workflow_run" ]; then if [ "$EVENT_NAME" != "workflow_run" ]; then
echo "run_agent=true" >> "$GITHUB_OUTPUT" head_sha="$(git rev-parse HEAD)"
echo "base_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" review_base="$(git rev-parse "${head_sha}^" 2>/dev/null || printf '%s' "$head_sha")"
{
echo "run_agent=true"
echo "base_sha=${head_sha}"
echo "review_base_sha=${review_base}"
echo "review_head_sha=${head_sha}"
} >> "$GITHUB_OUTPUT"
exit 0 exit 0
fi fi
@@ -73,17 +79,65 @@ jobs:
exit 0 exit 0
fi fi
echo "run_agent=true" >> "$GITHUB_OUTPUT" runs_json="$RUNNER_TEMP/docs-agent-runs.json"
echo "base_sha=${remote_main}" >> "$GITHUB_OUTPUT" gh api --method GET "repos/${GITHUB_REPOSITORY}/actions/workflows/docs-agent.yml/runs" \
-f branch=main \
-f event=workflow_run \
-f per_page=100 > "$runs_json"
one_hour_ago="$(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ)"
recent_runs="$(
jq -r \
--argjson current_run_id "$GITHUB_RUN_ID" \
--arg one_hour_ago "$one_hour_ago" \
'.workflow_runs[]
| select(.database_id != $current_run_id)
| select(.created_at >= $one_hour_ago)
| select(.status != "cancelled")
| select((.conclusion // "") != "skipped")
| [.database_id, .status, (.conclusion // ""), .created_at, .head_sha]
| @tsv' "$runs_json"
)"
if [ -n "$recent_runs" ]; then
echo "Docs agent already ran or is running within the last hour; skipping."
printf '%s\n' "$recent_runs"
echo "run_agent=false" >> "$GITHUB_OUTPUT"
exit 0
fi
review_base="$(
jq -r \
--argjson current_run_id "$GITHUB_RUN_ID" \
--arg remote_main "$remote_main" \
'.workflow_runs[]
| select(.database_id != $current_run_id)
| select(.status != "cancelled")
| select((.conclusion // "") != "skipped")
| .head_sha
| select(. != null and . != "")
| select(. != $remote_main)
' "$runs_json" | head -n 1
)"
if [ -z "$review_base" ] || ! git cat-file -e "${review_base}^{commit}" 2>/dev/null; then
review_base="$(git rev-parse "${remote_main}^" 2>/dev/null || printf '%s' "$remote_main")"
fi
{
echo "run_agent=true"
echo "base_sha=${remote_main}"
echo "review_base_sha=${review_base}"
echo "review_head_sha=${remote_main}"
} >> "$GITHUB_OUTPUT"
- name: Setup Node environment - name: Setup Node environment
if: steps.superseded.outputs.run_agent == 'true' if: steps.gate.outputs.run_agent == 'true'
uses: ./.github/actions/setup-node-env uses: ./.github/actions/setup-node-env
with: with:
install-bun: "false" install-bun: "false"
- name: Ensure docs agent key exists - name: Ensure docs agent key exists
if: steps.superseded.outputs.run_agent == 'true' if: steps.gate.outputs.run_agent == 'true'
env: env:
OPENAI_API_KEY: ${{ secrets.OPENCLAW_DOCS_AGENT_OPENAI_API_KEY || secrets.OPENAI_API_KEY }} OPENAI_API_KEY: ${{ secrets.OPENCLAW_DOCS_AGENT_OPENAI_API_KEY || secrets.OPENAI_API_KEY }}
run: | run: |
@@ -94,8 +148,11 @@ jobs:
fi fi
- name: Run Codex docs agent - name: Run Codex docs agent
if: steps.superseded.outputs.run_agent == 'true' if: steps.gate.outputs.run_agent == 'true'
uses: openai/codex-action@v1 uses: openai/codex-action@v1
env:
DOCS_AGENT_BASE_SHA: ${{ steps.gate.outputs.review_base_sha }}
DOCS_AGENT_HEAD_SHA: ${{ steps.gate.outputs.review_head_sha }}
with: with:
openai-api-key: ${{ secrets.OPENCLAW_DOCS_AGENT_OPENAI_API_KEY || secrets.OPENAI_API_KEY }} openai-api-key: ${{ secrets.OPENCLAW_DOCS_AGENT_OPENAI_API_KEY || secrets.OPENAI_API_KEY }}
prompt-file: .github/codex/prompts/docs-agent.md prompt-file: .github/codex/prompts/docs-agent.md
@@ -106,7 +163,7 @@ jobs:
codex-args: '["--full-auto"]' codex-args: '["--full-auto"]'
- name: Enforce existing-docs-only patch - name: Enforce existing-docs-only patch
if: steps.superseded.outputs.run_agent == 'true' if: steps.gate.outputs.run_agent == 'true'
run: | run: |
set -euo pipefail set -euo pipefail
@@ -139,8 +196,8 @@ jobs:
fi fi
- name: Restore Node 24 path - name: Restore Node 24 path
if: steps.superseded.outputs.run_agent == 'true' if: steps.gate.outputs.run_agent == 'true'
run: | run: | # zizmor: ignore[github-env] NODE_BIN is set by the trusted local setup-node-env action in this same job
set -euo pipefail set -euo pipefail
export PATH="${NODE_BIN}:${PATH}" export PATH="${NODE_BIN}:${PATH}"
echo "${NODE_BIN}" >> "$GITHUB_PATH" echo "${NODE_BIN}" >> "$GITHUB_PATH"
@@ -149,13 +206,13 @@ jobs:
pnpm -v pnpm -v
- name: Check docs - name: Check docs
if: steps.superseded.outputs.run_agent == 'true' if: steps.gate.outputs.run_agent == 'true'
run: pnpm check:docs run: pnpm check:docs
- name: Commit docs updates - name: Commit docs updates
if: steps.superseded.outputs.run_agent == 'true' if: steps.gate.outputs.run_agent == 'true'
env: env:
BASE_SHA: ${{ steps.superseded.outputs.base_sha }} BASE_SHA: ${{ steps.gate.outputs.base_sha }}
GITHUB_TOKEN: ${{ github.token }} GITHUB_TOKEN: ${{ github.token }}
TARGET_BRANCH: main TARGET_BRANCH: main
run: | run: |

View File

@@ -23,6 +23,15 @@ listed PRs when `apply=true`. Before mutating GitHub, it verifies that the
landed PR is merged and that each duplicate has either a shared referenced issue landed PR is merged and that each duplicate has either a shared referenced issue
or overlapping changed hunks. or overlapping changed hunks.
The `Docs Agent` workflow is an event-driven Codex maintenance lane for keeping
existing docs aligned with recently landed changes. It has no pure schedule: a
successful non-bot push CI run on `main` can trigger it, and manual dispatch can
run it directly. Workflow-run invocations skip when `main` has moved on or when
another non-skipped Docs Agent run was created in the last hour. When it runs, it
reviews the commit range from the previous non-skipped Docs Agent source SHA to
current `main`, so one hourly run can cover all main changes accumulated since
the last docs pass.
The `Test Performance Agent` workflow is an event-driven Codex maintenance lane The `Test Performance Agent` workflow is an event-driven Codex maintenance lane
for slow tests. It has no pure schedule: a successful non-bot push CI run on for slow tests. It has no pure schedule: a successful non-bot push CI run on
`main` can trigger it, but it skips if another workflow-run invocation already `main` can trigger it, but it skips if another workflow-run invocation already