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>
This commit is contained in:
Bobby Noelte
2026-11-11 15:37:33 +01:00
co-authored by Claude Opus 5.5
parent 62cbf4cf2a
commit dd41e5d57c
18 changed files with 1394 additions and 9 deletions
+38
View File
@@ -0,0 +1,38 @@
## Summary
<!--
The smallest view that makes the change clear, next to short text: pseudocode, a call or file
tree, a diff sketch or a Mermaid diagram. Reference the issue, e.g. "Closes #123". Sections follow
the pr skill, see ai/skills/engineering/pr/SKILL.md.
-->
## Evidence
<!-- Before and after: a failing and passing test run, console output or a screenshot. -->
## Merge Danger
**Door:** <!-- one-way or two-way: can the change be walked back cheaply? -->
**Blast Radius:** <!-- one word: who or what is affected if it goes wrong -->
<!--
Optional, when they apply:
**Breaking:** what users, configurations or integrations must change.
**Security:** what the change means for security.
-->
## Checklist
- [ ] AI-assisted (an agent wrote or co-wrote part of this PR)
- [ ] I reviewed the complete diff myself (author review)
<!--
If the pull request is AI-assisted, CI requires the section below with the full SHA of the head
commit in its Reviewed: line. The agent writes the section from its self-review of the current head
commit. Before every push, rerun the self-review and rewrite the section, do not just edit the
SHA; replace the body right after the push. See docs/develop/ai.md.
Delete the section if the pull request is not AI-assisted.
-->
## Self-review
+111
View File
@@ -0,0 +1,111 @@
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}"