feat: add ready-for-dev issue and PR readiness gates - #380
Conversation
Add an issue readiness workflow that manages the ready-for-dev label via type-specific criteria (bugs need a reproducible uv/pytest/python/curl command plus acceptance-criteria checklist; enhancements need desired behavior plus checklist), with idempotent upserted feedback comments and not-ready results handled safely under set -euo pipefail. Add a pull_request_target gate that blocks PRs whose linked issues are not ready-for-dev (grandfathering pre-rollout issues), re-run for all linked open PRs whenever the label changes. Add matching issue forms (bug, feature request) and pytest coverage for the three CI scripts. Fixes #379 Co-authored-by: openhands <openhands@all-hands.dev>
|
🤖 OpenHands is reviewing this PR. Head commit: This comment was posted by an AI agent (OpenHands). |
all-hands-bot
left a comment
There was a problem hiding this comment.
This review was created by an AI agent (OpenHands) on behalf of the repository maintainers.
Two material issues, both in the async "refresh" path and the linked-issue gate's 404 handling. The readiness-check core (check_issue_readiness.py), the PR-body parser, and the pull_request_target checkout-from-base security posture all look sound; the 30 new tests pass locally.
Risk assessment
Medium. Neither issue blocks the happy path (issues earn/lose ready-for-dev correctly; PRs linking ready or grandfathered issues gate correctly), but the two findings below cause silent false-negatives/positives in edge cases the PR is explicitly designed to handle.
1. refresh-linked-pr-gates job is missing issues: read (.github/workflows/issue-readiness-check.yml)
The job's permissions block grants only contents: read, pull-requests: read, and actions: write. But refresh_linked_pr_checks.py::_linked_open_prs runs gh api graphql against repository.issue(number).timelineItems, which requires issues: read. Without it the query fails, _run returns nonzero, and _linked_open_prs logs a ::warning:: and returns [] — so the whole "re-run PR description checks so the gate never goes stale" feature silently does nothing. Add issues: read to that job's permissions.
2. A nonexistent linked issue is silently ignored unless every link 404s (.github/scripts/check_pr_description.py)
In validate_linked_issue_ready, a 404 on fetch_issue_details is swallowed with continue, and the "could not be found" error is only emitted when not checked (i.e. all referenced issues 404). So a PR body like Fixes #123 where #123 is a typo or doesn't exist, combined with one valid Fixes #12, passes the gate instead of failing closed. Since the gate's purpose is to keep linked work honest, a nonexistent reference should fail the check regardless of how many other valid links exist. Consider collecting 404'd numbers and reporting them alongside not_ready_new rather than skipping them.
Summary
Adds the
ready-for-devreadiness gates for issues and linked PRs, adapted to this repository's development workflow (uv,pytest,python,curl), addressing #379.What was added
.github/workflows/issue-readiness-check.yml— manages theready-for-devlabel onissuesevents:buglabel):### Actual Behaviormust reference a reproducible command from this repo's supported dev workflow (uv,pytest,python,curl) and### Acceptance Criteriamust contain at least one checklist item.enhancementlabel): non-empty### Desired Behaviorand### Acceptance Criteria(with a checklist item).--jsonmode always exits 0, so a not-ready issue does not abort the workflow underset -euo pipefail— label removal and the feedback comment still run.refresh-linked-pr-gatesjob re-runs the PR description check for every open PR linking the issue wheneverready-for-devis added/removed, so the PR gate never goes stale..github/workflows/pr-description-check.yml— apull_request_targetgate (checking out trusted base-branch code only) that fails when a PR's linked issues (Fixes #N/Closes #N/Resolves #N, or bare#Nunder an## Issue Numberheading) are missingready-for-dev. Bot PRs are exempt; issues created before the rollout date (2026-08-26, day after the expected merge — happy to adjust) are grandfathered so existing work is not blocked retroactively. PRs that link no issues pass..github/scripts/—check_issue_readiness.py,check_pr_description.py,refresh_linked_pr_checks.py,post-readiness-comment.mjs(pattern adapted fromOpenHands/software-agent-sdk)..github/ISSUE_TEMPLATE/— bug and feature-request forms producing the sections the readiness check parses.tests/— 30 pytest tests for the three Python scripts (readiness criteria,set -euo pipefail-safe exit codes, linked-issue extraction, grandfathering, label-transition refresh logic), loaded viaimportliblike the SDK repo'stests/crosssuite.Testing
uv run pytest tests/test_check_issue_readiness.py tests/test_check_pr_description.py tests/test_refresh_linked_pr_checks.py— 30 passed.docker.errorsfrom testcontainers-based tests — no Docker daemon in this environment; CI runners have Docker).pre-commit runon all new files: yamlfmt, ruff format/lint, pycodestyle, pyright all pass.set -euo pipefailfor both ready and not-ready payloads, and the PR gate live against the GitHub API (grandfathered issue passes; nonexistent issue reference fails).Notes
main(issuesevents andpull_request_targetread workflows from the default branch), so they cannot gate this PR itself.This PR was created by an AI agent (OpenHands) on behalf of the user.
@neubig can click here to continue refining the PR