chore(agents): upgrade DWP to 2.17.0 + AI Diff Reviewer Flow B [skip release] #6
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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." |