Skip to content

chore(agents): upgrade DWP to 2.17.0 + AI Diff Reviewer Flow B [skip release] #6

chore(agents): upgrade DWP to 2.17.0 + AI Diff Reviewer Flow B [skip release]

chore(agents): upgrade DWP to 2.17.0 + AI Diff Reviewer Flow B [skip release] #6

Workflow file for this run

name: PR review
# AI-driven pre-merge review powered by `DailybotHQ/ai-diff-reviewer@v2.0.0`
# (marketplace listing: "AI Diff Reviewer"). Cursor-only, label-gated on
# `Ready`, gated on write-tier authorship (author-association whitelist),
# stable-named merge gate for branch protection. Runs the same review
# methodology as the vendored `ai-diff-reviewer` skill when
# `.review/extension.md` is present — the skill's `prompt.md` is
# byte-identical to the Action's shipped `prompts/default.md` at the same
# tag (enforced by upstream CI). CI additionally runs Iteration-Aware
# Review (IAR); local skill reviews stay a full pass.
#
# Post-CI walkthrough: after this workflow posts its review, developers can
# invoke the skill's `apply-review` sub-skill locally to read the findings
# and walk through them per-finding (apply / defer / skip). Read-only by
# default; edits require per-finding consent; never commits or pushes.
#
# Trigger: `opened`, `labeled` on PRs targeting `main`. NOT `synchronize` —
# push does not re-review; toggle the `Ready` label to re-run. Applying
# `skip-ai-review` while `Ready` is already present also re-runs (bypass).
#
# Secrets required (repo settings > Secrets):
# * CURSOR_API_KEY — Cursor subscription key (unlimited on Pro).
# Without it the review job is skipped (should_run=false) and the merge
# gate fails loud ("no provider secret configured"). Fork PRs never see
# repo secrets (GitHub behavior on `pull_request` runs), so fork heads are
# skipped with their own reason instead of failing on a phantom secret.
#
# Labels used (auto-created on first run):
# * Ready — apply to unlock the review (matched case-insensitively).
# * pr-reviewed — applied automatically after a successful, non-skipped review.
# * skip-ai-review — opt-in emergency bypass (no LLM; check succeeds). Protect
# with a repository ruleset if this gate is required.
on:
pull_request:
branches: [main]
# Deliberately NOT `synchronize` (cost control): commits pushed after a
# review do NOT re-trigger it, and `AI review gate` stays green while
# unaudited commits accumulate. After every fix push, a maintainer must
# remove + re-apply `Ready` to force a fresh review — see
# docs/PR_REVIEW_WORKFLOW.md § "Re-running the review after a fix push".
types: [opened, labeled]
concurrency:
group: pr-review-${{ github.event.pull_request.number }}
cancel-in-progress: true
permissions:
contents: read
pull-requests: write
jobs:
scope:
name: 'Decide review scope'
runs-on: ubuntu-latest
outputs:
should_run: ${{ steps.decide.outputs.should_run }}
empty_reason: ${{ steps.decide.outputs.empty_reason }}
steps:
- name: Decide whether to run
id: decide
shell: bash
env:
EVENT_ACTION: ${{ github.event.action }}
EVENT_LABEL: ${{ github.event.label.name }}
LABELS_JSON: ${{ toJSON(github.event.pull_request.labels.*.name) }}
AUTHOR_ASSOC: ${{ github.event.pull_request.author_association }}
HAS_CURSOR: ${{ secrets.CURSOR_API_KEY != '' }}
IS_FORK: ${{ github.event.pull_request.head.repo.full_name != github.repository }}
run: |
set -euo pipefail
# Cheapest gate first: author-association write-tier. External-contributor
# PRs from forks can never burn tokens — the field comes from the webhook
# payload and cannot be spoofed.
case "$AUTHOR_ASSOC" in
OWNER|MEMBER|COLLABORATOR) : ;;
*)
echo "::notice::PR author association is '$AUTHOR_ASSOC' — write-tier gate rejected (only OWNER/MEMBER/COLLABORATOR run). Skipping review."
{
echo 'should_run=false'
echo 'empty_reason=author-association'
} >> "$GITHUB_OUTPUT"
exit 0 ;;
esac
# Ready-label gate (case-insensitive). `labeled` event runs when the
# label just added is `Ready`, OR when `skip-ai-review` is added while
# `Ready` is already present (emergency-bypass re-run). `opened` runs
# if the PR already carries `Ready`.
lc() { tr '[:upper:]' '[:lower:]'; }
ready_present=false
if printf '%s' "$LABELS_JSON" | lc | grep -qE '"ready"'; then
ready_present=true
fi
should_run=false
case "$EVENT_ACTION" in
labeled)
label_lc="$(printf '%s' "$EVENT_LABEL" | lc)"
if [ "$label_lc" = "ready" ]; then
should_run=true
elif [ "$label_lc" = "skip-ai-review" ] && [ "$ready_present" = "true" ]; then
should_run=true
fi
;;
opened)
[ "$ready_present" = "true" ] && should_run=true ;;
esac
if [ "$should_run" != "true" ]; then
echo "::notice::Apply the 'Ready' label to trigger the review (remove + re-add to re-run). Optional: 'skip-ai-review' while Ready is present for emergency bypass."
{
echo 'should_run=false'
echo 'empty_reason=no-ready-label'
} >> "$GITHUB_OUTPUT"
exit 0
fi
# Fork gate. GitHub never exposes repo secrets to `pull_request` runs
# whose head lives in a fork — even for OWNER/MEMBER/COLLABORATOR
# authors. Without this check those PRs would fall through to the
# secret gate and fail with a misleading "secret not configured"
# error. Skip (gate job is skipped too) with an actionable notice.
if [ "$IS_FORK" = "true" ]; then
echo "::notice::PR head is a fork — repo secrets are unavailable on fork pull_request runs, so the AI review cannot execute. Push the branch to this repository and re-apply 'Ready' to get a review."
{
echo 'should_run=false'
echo 'empty_reason=fork-no-secrets'
} >> "$GITHUB_OUTPUT"
exit 0
fi
# Provider secret gate. If CURSOR_API_KEY is absent, the merge gate
# will fail loud (see gate job) with a clear message.
if [ "$HAS_CURSOR" != "true" ]; then
echo "::error::CURSOR_API_KEY is not configured on this repo. Set it in Settings > Secrets to unblock the merge gate."
{
echo 'should_run=false'
echo 'empty_reason=no-provider-secret'
} >> "$GITHUB_OUTPUT"
exit 0
fi
echo "::notice::Review scope: run=true (author=$AUTHOR_ASSOC, Ready=applied, secret=configured)"
{
echo 'should_run=true'
echo 'empty_reason='
} >> "$GITHUB_OUTPUT"
labels-bootstrap:
name: 'Bootstrap review labels'
needs: scope
# Only bootstrap when we're actually going to review — no work otherwise.
if: needs.scope.outputs.should_run == 'true'
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- name: Ensure review labels exist
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
run: |
set -euo pipefail
# Idempotent — GitHub returns a friendly error if the label exists;
# `|| true` absorbs it.
# Colors: Ready=#0e8a16 (green), pr-reviewed=#0366d6 (blue),
# skip-ai-review=#b60205 (red — emergency bypass).
gh label create Ready --repo "$REPO" --color 0e8a16 --description "Trigger AI code review" || true
gh label create pr-reviewed --repo "$REPO" --color 0366d6 --description "Passed AI code review" || true
gh label create skip-ai-review --repo "$REPO" --color b60205 --description "Emergency bypass: skip AI Diff Reviewer LLM pass" || true
review:
name: 'AI review — cursor'
needs: [scope, labels-bootstrap]
if: needs.scope.outputs.should_run == 'true'
runs-on: ubuntu-latest
timeout-minutes: 25
steps:
- uses: actions/checkout@v6
with:
# `git diff origin/<base>...HEAD` requires the base ref locally.
fetch-depth: 0
# Do NOT persist GITHUB_TOKEN into .git/config — the Cursor CLI runs
# with broad local access; a persisted token on disk is an exfil
# surface. The reviewer talks to the GitHub API in-process.
persist-credentials: false
# Pinned to the exact tag recorded in skills-lock.json for the vendored
# skill — a floating @v2 could change prompts/default.md while the local
# skill stays at v2.0.0, silently breaking the parity claim above. Bump
# this pin and the vendored skill together, in one PR.
- uses: DailybotHQ/ai-diff-reviewer@v2.0.0
with:
provider: cursor
api-key: ${{ secrets.CURSOR_API_KEY }}
github-token: ${{ secrets.GITHUB_TOKEN }}
model: auto
label-gate: Ready
author-association: OWNER,MEMBER,COLLABORATOR
applied-label: pr-reviewed
skip-review-label: skip-ai-review
strictness: block-on-critical
prompt-extension-file: .review/extension.md
max-inline-comments: 15
gate:
# STABLE NAME — mark ONLY this as the required check in branch protection.
# A `skipped` required check counts as passing (GitHub behavior), so PRs
# without `Ready`, from non-write-tier authors, or from fork heads
# (fork-no-secrets) are mergeable without a review. To force reviews on every
# PR, pair this required check with a separate rule that enforces the
# `Ready` label. Emergency bypass via `skip-ai-review` makes the review
# job succeed (Action short-circuit) — protect that label with a ruleset.
name: 'AI review gate'
needs: [scope, review]
if: always() && needs.scope.outputs.empty_reason != 'no-ready-label' && needs.scope.outputs.empty_reason != 'author-association' && needs.scope.outputs.empty_reason != 'fork-no-secrets'
runs-on: ubuntu-latest
permissions:
actions: read
steps:
- name: Require the review leg to have passed
shell: bash
env:
SCOPE_RESULT: ${{ needs.scope.result }}
REVIEW_RESULT: ${{ needs.review.result }}
EMPTY_REASON: ${{ needs.scope.outputs.empty_reason }}
run: |
set -euo pipefail
if [ "$SCOPE_RESULT" != "success" ]; then
echo "::error::scope job did not succeed (result=$SCOPE_RESULT). Blocking merge."
exit 1
fi
if [ "$EMPTY_REASON" = "no-provider-secret" ]; then
echo "::error::Review requested (via 'Ready' label) but CURSOR_API_KEY is not configured. Set it in Settings > Secrets to unblock the merge gate."
exit 1
fi
if [ "$REVIEW_RESULT" != "success" ]; then
echo "::error::AI review did not pass (result=$REVIEW_RESULT). Address findings or toggle 'Ready' to re-run."
exit 1
fi
echo "AI review passed — merge gate satisfied."