diff --git a/.github/workflows/auto-approve-risto.yml b/.github/workflows/auto-approve-risto.yml new file mode 100644 index 0000000..fa71ba5 --- /dev/null +++ b/.github/workflows/auto-approve-risto.yml @@ -0,0 +1,89 @@ +name: Auto-approve Risto's reviews + +# Fires when Risto requests Carmen's review on a PR he authored. +# Generates a one-sentence approval in Carmen's voice via Anthropic API and +# posts it as Carmen's review (via CARMEN_REVIEW_PAT) so SOC 2 / ISO 27001 +# audit trails see Carmen as the approver, not github-actions[bot]. +# Does NOT merge, does NOT delete the branch. +# +# Also supports workflow_dispatch for manual validation: pass a PR number, +# the workflow runs the diff → AI → comment pipeline end-to-end but skips +# the actual approval post (dry run). Use to validate against any PR +# without leaving stray approval artifacts. + +on: + pull_request: + types: [review_requested] + workflow_dispatch: + inputs: + pr_number: + description: "PR number to dry-run against (no approval is posted)" + required: true + type: string + +permissions: + contents: read + pull-requests: read + +jobs: + approve: + if: > + github.event_name == 'workflow_dispatch' || + (github.event.requested_reviewer.login == 'ckivisild-elnora-ai' && + github.event.pull_request.user.login == 'rjamul-elnora-ai') + runs-on: ubuntu-latest + env: + PR_NUMBER: ${{ github.event.pull_request.number || inputs.pr_number }} + REPO: ${{ github.repository }} + steps: + - name: Fetch PR title and diff + id: pr + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + TITLE=$(gh pr view "$PR_NUMBER" --repo "$REPO" --json title -q .title) + echo "title=$TITLE" >> "$GITHUB_OUTPUT" + gh pr diff "$PR_NUMBER" --repo "$REPO" > /tmp/diff.patch || echo "(diff unavailable)" > /tmp/diff.patch + # Truncate at 50KB to stay within token budget for the generation step + head -c 50000 /tmp/diff.patch > /tmp/diff.trunc + mv /tmp/diff.trunc /tmp/diff.patch + echo "Diff size: $(wc -c < /tmp/diff.patch) bytes" + + - name: Generate Carmen-voice approval comment + env: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + PR_TITLE: ${{ steps.pr.outputs.title }} + run: | + DIFF_CONTENT=$(cat /tmp/diff.patch) + jq -n \ + --arg diff "$DIFF_CONTENT" \ + --arg title "$PR_TITLE" \ + '{ + model: "claude-haiku-4-5-20251001", + max_tokens: 80, + system: "You are Carmen Kivisild approving Risto'\''s PR. Write ONE short sentence in her voice: direct, plain prose, no em-dashes, no emojis, no filler, no rhetorical questions. Examples of the register: \"reviewed and approved, looks good\" / \"reviewed and approved, the loop fix was clever\" / \"reviewed and approved, clean refactor\". If something specific in the diff is genuinely clever or notable, mention it briefly. Otherwise default to a generic looks-good sentence. Output ONLY the sentence, no preamble.", + messages: [{ + role: "user", + content: ("PR title: " + $title + "\n\nDiff:\n" + $diff) + }] + }' > /tmp/request.json + curl -sS https://api.anthropic.com/v1/messages \ + -H "x-api-key: $ANTHROPIC_API_KEY" \ + -H "anthropic-version: 2023-06-01" \ + -H "content-type: application/json" \ + --data-binary @/tmp/request.json > /tmp/response.json + jq -r '.content[0].text // "reviewed and approved, looks good"' /tmp/response.json > /tmp/comment.txt + echo "--- generated comment ---" + cat /tmp/comment.txt + echo "" + + - name: Post approval as Carmen + # Skipped on workflow_dispatch — that path is dry-run-only + if: github.event_name == 'pull_request' + env: + GH_TOKEN: ${{ secrets.CARMEN_REVIEW_PAT }} + run: gh pr review "$PR_NUMBER" --repo "$REPO" --approve --body-file /tmp/comment.txt + + - name: Dry-run notice (workflow_dispatch only) + if: github.event_name == 'workflow_dispatch' + run: echo "::notice::Dry run — comment generated above but NOT posted as approval. Real review_requested events post for real."