Files
EOS/.github/workflows/ai-self-review.yml
T
Bobby NoelteandClaude Opus 5.5 dd41e5d57c chore(ai): add agent self-review and a CI check for agent-assisted PRs
Additional changes
Agents write a task plan to .scratch/<branch>/plan.md and self-review with code-review
Agents fix hard violations, report judgement calls and rerun the self-review before every push
Agents declare the review in a Self-review section of the pull request body
Add the ai-self-review workflow and scripts/check_pr_self_review.py for pull requests into main
Detect agents by Assisted-by or Anthropic Co-Authored-By trailers, body markers or the AI box
Fail only on a missing Self-review section or a missing, malformed or stale Reviewed SHA
Note an unticked AI-assisted box and malformed optional lines without failing
Comment on the pull request how to fix a failure and update that one comment on later runs
Show the same report in the job summary and accept Windows line endings in the body
Skip dependabot and release pull requests
Run on pull_request_target with the base branch's script, never pull request code
Add the pull request template: the pr skill's sections plus Checklist and Self-review
Require always valid optimization parameters in the energy-domain skill
Require datetimeutil for date and time handling in new and changed code in AGENTS.md
Require the Assisted-by trailer on agent commits in the commit-messages skill
Add tests for the check in tests/test_ai.py
Add GLOSSARY.md and the agent tracker docs in docs/agents/
Add decision record docs/adr/0001 with an ADR index in the docs toctree
Document the workflow in docs/develop/ai.md and CONTRIBUTING.md
Exclude docs/agents from Sphinx and PyMarkdown and ignore .scratch/
Document cz check instead of make gitlint for commit messages in docs/develop/ai.md

Assisted-by: Claude Code (claude-opus-5-5)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 15:37:33 +01:00

112 lines
4.4 KiB
YAML

name: ai-self-review
# Checks the self-review declaration of agent-assisted pull requests, see
# docs/adr/0001-self-declared-sha-bound-review-gate.md and docs/develop/ai.md.
#
# pull_request_target runs the workflow and the check script of the base branch, so a pull
# request cannot weaken its own check. The job never checks out or runs code of the pull
# request; it reads the body and the commit messages through the API only.
#
# It runs only for pull requests into main, never on pushes to main or to other branches, and not
# for pull requests into other branches.
#
# On failure it posts one comment that explains the fix, found again by its marker and updated in
# place; once the check passes, the comment is updated to say so. Commenting needs
# pull-requests: write, which is safe here because no pull request code runs.
on:
pull_request_target:
types: [opened, edited, synchronize, reopened]
branches: [main]
permissions:
contents: read
pull-requests: write
env:
# Must equal COMMENT_MARKER in scripts/check_pr_self_review.py (checked by tests/test_ai.py).
COMMENT_MARKER: "<!-- ai-self-review -->"
concurrency:
group: ai-self-review-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
self-review:
runs-on: ubuntu-latest
steps:
- name: Check out the base branch (not the pull request)
uses: actions/checkout@v7
with:
ref: ${{ github.event.pull_request.base.sha }}
persist-credentials: false
- uses: actions/setup-python@v7
with:
python-version-file: .python-version
- name: Fetch the pull request body and commit messages
env:
GH_TOKEN: ${{ github.token }}
PR_BODY: ${{ github.event.pull_request.body }}
PR_URL: repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}
run: |
printf '%s' "$PR_BODY" > "$RUNNER_TEMP/body.md"
gh api --paginate "$PR_URL/commits" --jq '.[].commit.message | @json' \
> "$RUNNER_TEMP/commits.jsonl"
- name: Check the self-review declaration
id: check
env:
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
PR_TITLE: ${{ github.event.pull_request.title }}
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
run: |
set +e
python scripts/check_pr_self_review.py \
--body-file "$RUNNER_TEMP/body.md" \
--commits-file "$RUNNER_TEMP/commits.jsonl" \
--head-sha "$PR_HEAD_SHA" \
--title "$PR_TITLE" \
--author "$PR_AUTHOR" \
--report-file "$RUNNER_TEMP/report.md"
status=$?
if [ ! -s "$RUNNER_TEMP/report.md" ]; then
# The script failed before it could write its report.
status=1
{
echo "$COMMENT_MARKER"
echo "### ❌ AI self-review check could not run"
echo
echo "The check script failed before it could check the declaration. This is a"
echo "problem of the check, not necessarily of the pull request; see the log of the"
echo "workflow run."
} > "$RUNNER_TEMP/report.md"
fi
echo "status=$status" >> "$GITHUB_OUTPUT"
cat "$RUNNER_TEMP/report.md" >> "$GITHUB_STEP_SUMMARY"
- name: Comment on the pull request
# A failing comment must not change the result of the check.
continue-on-error: true
env:
GH_TOKEN: ${{ github.token }}
STATUS: ${{ steps.check.outputs.status }}
ISSUE_URL: repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}
COMMENTS_URL: repos/${{ github.repository }}/issues/comments
run: |
comment_id=$(gh api --paginate "$ISSUE_URL/comments" --jq \
'.[] | select(.user.login == "github-actions[bot]")
| select(.body | startswith(env.COMMENT_MARKER)) | .id' | head -n 1)
if [ -n "$comment_id" ]; then
gh api --method PATCH "$COMMENTS_URL/$comment_id" -F "body=@$RUNNER_TEMP/report.md" \
> /dev/null
elif [ "$STATUS" != "0" ]; then
gh api --method POST "$ISSUE_URL/comments" -F "body=@$RUNNER_TEMP/report.md" > /dev/null
fi
- name: Report the result
if: always()
env:
STATUS: ${{ steps.check.outputs.status }}
run: |
if [ "${STATUS:-1}" != "0" ]; then
echo "::error::The self-review check failed; see the step summary or the pull request comment."
fi
exit "${STATUS:-1}"