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
4 changes: 4 additions & 0 deletions .claude/review-prompt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Claude output format

- Use inline comments at the relevant lines for specific issues.
- Use a top-level comment for the summary, severity-tagged finding list, AGENTS.md / writing_style_guide.md compliance check, and the verification assessment.
5 changes: 5 additions & 0 deletions .github/codex/pr-review.prompt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Codex output format

- Read `./.github/codex/pr-review-context.md` for PR metadata and the diff commands.
- Return a markdown PR comment starting with `## Codex Review`.
- Tag each finding with a severity (P0 / P1 / P2), file path, and line number when known confidently.
6 changes: 6 additions & 0 deletions .github/pi/pr-review.prompt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Pi output format

- Read `./.github/pi/pr-review-context.md` for PR metadata and the diff commands.
- Return a markdown PR comment starting with `## Pi Review`.
- Tag each finding with a severity (P0 / P1 / P2), file path, and line number when known confidently.
- Output ONLY the final review markdown — no preamble, no thinking, no tool transcripts.
57 changes: 57 additions & 0 deletions .github/review-prompt-shared.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Pull request review — shared policy (windmilldocs)

You are reviewing a GitHub pull request for the **windmilldocs** repository (the public Windmill documentation site, built with Docusaurus). Apply this policy alongside your tool's output requirements.

## Read the project rules first

- Read `AGENTS.md` (repo root), `CLAUDE.md`, and `writing_style_guide.md` before reviewing — they are the canonical contributor guide for documentation.
- Quote the exact rule from `AGENTS.md` or `writing_style_guide.md` when flagging a violation.

## Review policy

- Only report issues you are confident are real and introduced by this pull request.
- Focus on factual mistakes, broken links/anchors, MDX/markdown issues, missing or invalid frontmatter, sidebar misregistration, and clear `AGENTS.md` / `writing_style_guide.md` violations.
- Do not report style nits, speculative concerns, pre-existing issues, or anything Docusaurus's own build (`npm run build`) would obviously catch (build errors are caught by the `check docs build` workflow).
- Self-validate each finding before posting: "is this definitely a real issue?" If uncertain, discard it.
- Read additional files only when the diff is not enough to validate a finding.
- Do not modify any files.

## Severity triage

Tag each finding with a severity. Always report P0 and P1. Report P2 only when the diff invites it (a new docs page, a new section in `sidebars.js`, a new landing page, a new component, or a meaningful restructure).

- **P0** — factually wrong claim about a Windmill feature that could mislead users (e.g. wrong CLI flag, wrong default value, wrong tier gating like "this is free" when it's Enterprise), broken link to a critical onboarding page, secrets/credentials accidentally committed, security misinformation.
- **P1** — broken internal link or anchor, missing/invalid frontmatter `description`, page not registered in `sidebars.js` when it should be, image referenced but missing from the directory, code example that won't run as written, Enterprise/Cloud/Pro feature not labeled as such on first mention, missing `[Enterprise Edition](/pricing)` link on first mention of EE on the page.
- **P2** — `AGENTS.md` / `writing_style_guide.md` violations (em dashes, bold misuse, title case in headings, marketing-style language, missing backlinks on first mention of a concept), JSON-LD missing on a non-doc page that needs it, `description` outside the recommended length (120–160 chars for docs/changelog, up to ~300 for blog/case studies), missing `textAnswer` field on FAQ entries, image only in PNG (no WebP) or vice versa, file/folder naming that doesn't follow the underscore + numeric prefix convention.

## Documentation-specific checks

For each new or significantly edited `.md` / `.mdx` page in the diff, verify:

- (a) Frontmatter has a `description` field, between 120–160 chars for docs/changelog, ideally question-based ("How do I ...?").
- (b) The first mention of "Enterprise Edition" / "EE" links to `/pricing`. Subsequent mentions on the same page do not need to be linked.
- (c) Internal links use **relative paths** (e.g. `../../8_triggers/index.mdx`) on first mention of a concept, and external links are absolute URLs.
- (d) If the page introduces a new feature, a backlink exists from related main pages (and ideally a `DocCard` component on the parent index).
- (e) If a new page is added under `docs/`, it appears in `sidebars.js`. If it's a major feature, it should also be considered for `docs/core_concepts/index.mdx` and `src/components/pricing/FeatureList.js` (when EE/Cloud/Team).
- (f) Code blocks specify a language (`ts`, `python`, `bash`, etc.).
- (g) No em dashes ('—') in prose — use sentences without them or '-' instead.
- (h) Headings use sentence case ('Like this'), not title case ('Like This'). No bold in headings or titles. No HTML colour styling on titles.
- (i) Images live in the same directory as the related MDX file, with both PNG and optimized WebP versions, and meaningful filenames.

For new non-doc pages (landing/marketing, case studies, product pages, FAQ sections), also verify the JSON-LD `<script type="application/ld+json">` block is present in `<Head>` per the patterns in `AGENTS.md` (`SoftwareApplication`, `FAQPage`, `ItemList`, etc.) and that any rich/JSX FAQ content has a plain-text counterpart (`textAnswer`).

## Test coverage assessment

End your review with a short "Verification" section:

- Note whether `npm run build` was likely to succeed (broken links / anchors, missing files referenced from `sidebars.js`, MDX validity). The CI `npm check` workflow runs this — call out any failure mode you spot in the diff that would block CI.
- For non-doc page changes (React components, schema, config), describe what manual verification is still useful (navigate to the page locally, click the new link, confirm the FAQ renders, etc.).
- If the diff has no in-app surface to exercise (purely text edits, image swaps), say that plainly.

## Additional reviewer instructions

If the prompt or context includes an "Additional reviewer instructions" section, treat it as extra guidance from the human who triggered this review and follow it.

## Prior PR discussion

If the prompt or context includes a "Prior PR discussion" section, this PR has already received review activity. Look for your own previous comment, take it into account, focus on what changed in the latest commits, and do not repeat findings the human already pushed back on or addressed.
270 changes: 270 additions & 0 deletions .github/workflows/codex-pr-review.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
name: Codex Auto Review

on:
pull_request:
types: [ready_for_review, opened, synchronize]
workflow_call:
inputs:
pr_number:
description: 'PR number to review'
required: true
type: number
extra_prompt:
description: 'Additional reviewer instructions appended to the standard review prompt'
required: false
type: string
default: ''
triggered_by:
description: 'GitHub username that triggered this review (for audit only)'
required: false
type: string
default: ''
secrets:
CODEX_AUTH_JSON:
required: false

concurrency:
group: codex-review-${{ inputs.pr_number || github.event.pull_request.number }}
cancel-in-progress: true

jobs:
check-membership:
if: github.event_name == 'pull_request'
uses: windmill-labs/windmill/.github/workflows/check-org-membership.yml@main
with:
commenter: ${{ github.event.pull_request.user.login }}
secrets:
access_token: ${{ secrets.ORG_ACCESS_TOKEN }}

codex-review:
needs: check-membership
runs-on: ubuntu-latest
timeout-minutes: 30
if: |
always() &&
(
needs.check-membership.result == 'skipped' ||
(needs.check-membership.result == 'success' && needs.check-membership.outputs.is_member == 'true')
) &&
(
github.event_name == 'workflow_call' ||
(github.event.pull_request.draft == false && github.event.pull_request.head.repo.fork == false)
)
permissions:
contents: read
issues: write
pull-requests: write
steps:
- name: Check Codex configuration
id: codex_config
env:
CODEX_AUTH_JSON: ${{ secrets.CODEX_AUTH_JSON }}
run: |
if [ -n "$CODEX_AUTH_JSON" ]; then
echo "enabled=true" >> "$GITHUB_OUTPUT"
else
echo "enabled=false" >> "$GITHUB_OUTPUT"
echo "CODEX_AUTH_JSON is not configured; skipping Codex review."
fi

- name: Resolve PR metadata
if: steps.codex_config.outputs.enabled == 'true'
id: pr
env:
GH_TOKEN: ${{ github.token }}
INPUT_PR_NUMBER: ${{ inputs.pr_number }}
EVENT_PR_NUMBER: ${{ github.event.pull_request.number }}
EVENT_BASE_REF: ${{ github.event.pull_request.base.ref }}
EVENT_BASE_SHA: ${{ github.event.pull_request.base.sha }}
EVENT_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
EVENT_TITLE: ${{ github.event.pull_request.title }}
EVENT_BODY: ${{ github.event.pull_request.body }}
EVENT_FORK: ${{ github.event.pull_request.head.repo.fork }}
run: |
if [ -n "$INPUT_PR_NUMBER" ]; then
PR_JSON=$(gh pr view "$INPUT_PR_NUMBER" --repo "${{ github.repository }}" \
--json number,baseRefName,baseRefOid,headRefOid,title,body,isCrossRepository)
PR_NUMBER=$(echo "$PR_JSON" | jq -r '.number')
BASE_REF=$(echo "$PR_JSON" | jq -r '.baseRefName')
BASE_SHA=$(echo "$PR_JSON" | jq -r '.baseRefOid')
HEAD_SHA=$(echo "$PR_JSON" | jq -r '.headRefOid')
PR_TITLE=$(echo "$PR_JSON" | jq -r '.title')
PR_BODY=$(echo "$PR_JSON" | jq -r '.body // ""')
IS_FORK=$(echo "$PR_JSON" | jq -r '.isCrossRepository')
else
PR_NUMBER="$EVENT_PR_NUMBER"
BASE_REF="$EVENT_BASE_REF"
BASE_SHA="$EVENT_BASE_SHA"
HEAD_SHA="$EVENT_HEAD_SHA"
PR_TITLE="$EVENT_TITLE"
PR_BODY="$EVENT_BODY"
IS_FORK="$EVENT_FORK"
fi
if [ "$IS_FORK" = "true" ]; then
echo "Skipping Codex review for fork PR."
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
{
echo "skip=false"
echo "pr_number=$PR_NUMBER"
echo "base_ref=$BASE_REF"
echo "base_sha=$BASE_SHA"
echo "head_sha=$HEAD_SHA"
echo 'title<<PR_TITLE_EOF'
printf '%s\n' "$PR_TITLE"
echo 'PR_TITLE_EOF'
echo 'body<<PR_BODY_EOF'
printf '%s\n' "$PR_BODY"
echo 'PR_BODY_EOF'
} >> "$GITHUB_OUTPUT"

- name: Checkout repository
if: steps.codex_config.outputs.enabled == 'true' && steps.pr.outputs.skip != 'true'
uses: actions/checkout@v5
with:
ref: refs/pull/${{ steps.pr.outputs.pr_number }}/merge
fetch-depth: 1

- name: Set up Node.js
if: steps.codex_config.outputs.enabled == 'true' && steps.pr.outputs.skip != 'true'
uses: actions/setup-node@v4
with:
node-version: 22

- name: Install Codex CLI
if: steps.codex_config.outputs.enabled == 'true' && steps.pr.outputs.skip != 'true'
run: npm install --global @openai/codex@0.128.0

- name: Configure file-backed Codex auth
if: steps.codex_config.outputs.enabled == 'true' && steps.pr.outputs.skip != 'true'
env:
CODEX_AUTH_JSON: ${{ secrets.CODEX_AUTH_JSON }}
run: |
CODEX_HOME="$HOME/.codex"
echo "CODEX_HOME=$CODEX_HOME" >> "$GITHUB_ENV"
mkdir -p "$CODEX_HOME"
chmod 700 "$CODEX_HOME"
cat > "$CODEX_HOME/config.toml" <<'EOF'
cli_auth_credentials_store = "file"
EOF
printf '%s' "$CODEX_AUTH_JSON" > "$CODEX_HOME/auth.json"
chmod 600 "$CODEX_HOME/auth.json"
node -e 'JSON.parse(require("fs").readFileSync(process.argv[1], "utf8"))' "$CODEX_HOME/auth.json"

- name: Pre-fetch base and head refs for the PR
if: steps.codex_config.outputs.enabled == 'true' && steps.pr.outputs.skip != 'true'
env:
PR_BASE_REF: ${{ steps.pr.outputs.base_ref }}
PR_NUMBER: ${{ steps.pr.outputs.pr_number }}
run: |
git fetch --no-tags origin \
"$PR_BASE_REF" \
"+refs/pull/$PR_NUMBER/head"

- name: Fetch prior PR discussion
if: steps.codex_config.outputs.enabled == 'true' && steps.pr.outputs.skip != 'true'
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
PR_NUMBER: ${{ steps.pr.outputs.pr_number }}
run: |
gh api "repos/$REPO/issues/$PR_NUMBER/comments?per_page=100" \
--jq '[.[] | {user: .user.login, created_at: .created_at, body: (.body | .[:4000])}] | sort_by(.created_at) | .[-20:]' \
> prior-comments.json || echo "[]" > prior-comments.json

- name: Write Codex review context
if: steps.codex_config.outputs.enabled == 'true' && steps.pr.outputs.skip != 'true'
env:
PR_REPOSITORY: ${{ github.repository }}
PR_NUMBER: ${{ steps.pr.outputs.pr_number }}
PR_BASE_SHA: ${{ steps.pr.outputs.base_sha }}
PR_HEAD_SHA: ${{ steps.pr.outputs.head_sha }}
PR_TITLE: ${{ steps.pr.outputs.title }}
PR_BODY: ${{ steps.pr.outputs.body }}
EXTRA_PROMPT: ${{ inputs.extra_prompt }}
run: |
mkdir -p .github/codex
node <<'NODE'
const fs = require('fs');
const lines = [
`Repository: ${process.env.PR_REPOSITORY}`,
`PR number: ${process.env.PR_NUMBER}`,
`Base SHA: ${process.env.PR_BASE_SHA}`,
`Head SHA: ${process.env.PR_HEAD_SHA}`,
'',
'PR title:',
process.env.PR_TITLE || '(empty)',
'',
'PR body:',
process.env.PR_BODY || '(empty)',
'',
'Changed commits command:',
`git log --oneline ${process.env.PR_BASE_SHA}...${process.env.PR_HEAD_SHA}`,
'',
'Changed files command:',
`git diff --stat ${process.env.PR_BASE_SHA}...${process.env.PR_HEAD_SHA}`,
'',
'Full review diff command:',
`git diff --unified=0 ${process.env.PR_BASE_SHA}...${process.env.PR_HEAD_SHA}`
];
if (process.env.EXTRA_PROMPT && process.env.EXTRA_PROMPT.trim()) {
lines.push('', 'Additional reviewer instructions:', process.env.EXTRA_PROMPT.trim());
}
if (fs.existsSync('prior-comments.json')) {
try {
const comments = JSON.parse(fs.readFileSync('prior-comments.json', 'utf8'));
if (Array.isArray(comments) && comments.length > 0) {
lines.push(
'',
'Prior PR discussion (most recent up to 20 comments):',
'',
'If you have already reviewed this PR (look for your own earlier "## Codex Review" comment), focus on what changed since then per the diff and respect any decisions the human made in replies. Do not re-flag findings the human already pushed back on.',
''
);
for (const c of comments) {
lines.push(`### @${c.user} (${c.created_at})`, '', c.body, '', '---', '');
}
}
} catch (_) {}
}
fs.writeFileSync('.github/codex/pr-review-context.md', `${lines.join('\n')}\n`);
NODE

- name: Run Codex review
if: steps.codex_config.outputs.enabled == 'true' && steps.pr.outputs.skip != 'true'
run: |
cat .github/review-prompt-shared.md .github/codex/pr-review.prompt.md > /tmp/codex-prompt.md
codex exec \
-C "$GITHUB_WORKSPACE" \
-m gpt-5.5 \
-c 'model_reasoning_effort="xhigh"' \
-s danger-full-access \
-o codex-final-message.md \
- < /tmp/codex-prompt.md

- name: Post Codex review comment
if: steps.codex_config.outputs.enabled == 'true' && steps.pr.outputs.skip != 'true'
uses: actions/github-script@v7
env:
PR_NUMBER: ${{ steps.pr.outputs.pr_number }}
with:
github-token: ${{ github.token }}
script: |
const fs = require('fs');
const path = `${process.env.GITHUB_WORKSPACE}/codex-final-message.md`;
if (!fs.existsSync(path)) {
core.info('Codex did not produce a final message; skipping PR comment.');
return;
}
const body = fs.readFileSync(path, 'utf8').trim();
if (!body) {
core.info('Codex final message was empty; skipping PR comment.');
return;
}
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: Number(process.env.PR_NUMBER),
body,
});
Loading
Loading