Require human context at the top of PR descriptions (#14527)

Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
Engel Nyst
2026-05-22 18:42:40 +02:00
committed by GitHub
co-authored by openhands
parent 4c28c62c75
commit 0ddbd85be4
3 changed files with 145 additions and 3 deletions
+5
View File
@@ -2,8 +2,13 @@
<!-- AI/LLM agents: be concise and specific. Do not check the box below. -->
HUMAN:
- [ ] A human has tested these changes.
AGENT:
---
## Why
+137 -3
View File
@@ -2,11 +2,11 @@
name: PR Readiness Confirmation
on:
# Trigger when a PR is opened (non-draft) or converted from draft to ready.
# Trigger when a PR is opened, edited while ready for review, or converted from draft.
pull_request:
types: [opened, ready_for_review]
types: [opened, edited, ready_for_review]
pull_request_target:
types: [opened, ready_for_review]
types: [opened, edited, ready_for_review]
# Daily check for stale unconfirmed PRs.
schedule:
@@ -14,13 +14,147 @@ on:
permissions:
contents: read
issues: write
pull-requests: write
jobs:
# ── Require a short human-written summary on ready-for-review PRs ──
check-human-context:
if: |
github.event_name != 'schedule' &&
github.event.pull_request.draft == false &&
(
(
github.event_name == 'pull_request' &&
github.event.pull_request.head.repo.full_name == github.repository
) ||
(
github.event_name == 'pull_request_target' &&
github.event.pull_request.head.repo.full_name != github.repository
)
)
concurrency:
group: pr-human-context-${{ github.event.pull_request.number }}
cancel-in-progress: true
runs-on: ubuntu-24.04
steps:
- name: Validate HUMAN section
uses: actions/github-script@v9
with:
script: |
const reminderMarker = '<!-- pr-human-context-required -->';
const humanTestedCheckboxText = 'A human has tested these changes.';
const pr = context.payload.pull_request;
const issueNumber = pr.number;
const owner = context.repo.owner;
const repo = context.repo.repo;
const body = pr.body || '';
const escapeRegex = (text) =>
text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const humanSectionPattern = new RegExp(
`(?:^|\\n)HUMAN:\\s*\\n([\\s\\S]*?)(?=\\n(?:- )?\\[[ xX]\\] ${escapeRegex(humanTestedCheckboxText)})`
);
const warnOnly = async (label, operation) => {
try {
return await operation();
} catch (error) {
core.warning(`${label}: ${error.message}`);
return null;
}
};
const extractHumanSection = (text) => {
const match = text.match(humanSectionPattern);
if (!match) {
return '';
}
return match[1].replace(/<!--[\s\S]*?-->/g, '').trim();
};
const humanSection = extractHumanSection(body);
const hasHumanText = humanSection
.split(/\r?\n/)
.some((line) => line.trim().length > 0);
const comments = await warnOnly('Failed to list PR comments', () =>
github.paginate(github.rest.issues.listComments, {
owner,
repo,
issue_number: issueNumber,
})
);
if (!comments) {
return;
}
const existingReminder = comments.find((comment) =>
comment.body.includes(reminderMarker)
);
if (hasHumanText) {
if (existingReminder) {
await warnOnly('Failed to delete HUMAN reminder comment', () =>
github.rest.issues.deleteComment({
owner,
repo,
comment_id: existingReminder.id,
})
);
}
const reactions = await warnOnly('Failed to list PR reactions', () =>
github.paginate(github.rest.reactions.listForIssue, {
owner,
repo,
issue_number: issueNumber,
})
);
if (!reactions) {
return;
}
const botAlreadyReacted = reactions.some(
(reaction) =>
reaction.user?.login === 'github-actions[bot]' &&
reaction.content === '+1'
);
if (!botAlreadyReacted) {
await warnOnly('Failed to add HUMAN thumbs up reaction', () =>
github.rest.reactions.createForIssue({
owner,
repo,
issue_number: issueNumber,
content: '+1',
})
);
}
return;
}
if (!existingReminder) {
await warnOnly('Failed to create HUMAN reminder comment', () =>
github.rest.issues.createComment({
owner,
repo,
issue_number: issueNumber,
body: [
reminderMarker,
'👋 Thanks for opening this PR!',
'',
'Before review, please add a short note in the `HUMAN:` section at the top of the PR description telling maintainers, in your own words, what this PR does.',
'',
`Please put that note between \`HUMAN:\` and the \`- [ ] ${humanTestedCheckboxText}\` checkbox.`,
].join('\n'),
})
);
}
# ── Post a confirmation request when a PR becomes ready for review ──
ask-confirmation:
if: |
github.event_name != 'schedule' &&
github.event.action != 'edited' &&
github.event.pull_request.draft == false &&
(
(
+3
View File
@@ -285,6 +285,9 @@ Each integration follows a consistent pattern with service classes, storage mode
## Template for Github Pull Request
If you are starting a pull request (PR), please follow the template in `.github/pull_request_template.md`.
- The PR template now starts with a `HUMAN:` section, the human-tested checkbox, and an `AGENT:` section.
- `.github/workflows/pr-readiness-confirm.yml` checks non-draft PRs for non-empty text between `HUMAN:` and the human-tested checkbox; if present it adds a 👍 reaction, and if absent it posts a reminder comment.
## Implementation Details