Skip to content

docs: define issue-first contribution workflow - #6809

Open
lorenzejay wants to merge 2 commits into
mainfrom
lorenze/imp/issue-driven-pull-requests
Open

docs: define issue-first contribution workflow#6809
lorenzejay wants to merge 2 commits into
mainfrom
lorenze/imp/issue-driven-pull-requests

Conversation

@lorenzejay

@lorenzejay lorenzejay commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Note

Low Risk
Changes are limited to GitHub docs, templates, and Actions; no runtime library code. The gate uses privileged pull_request_target but only runs trusted default-branch script logic and does not execute PR code.

Overview
Introduces an issue-first contribution pilot: issues move through lifecycle labels until state:ready, and new PRs should reference exactly one ready issue via Implements #123 (not closing keywords) so issues stay open for post-merge verification.

Docs and templates expand CONTRIBUTING.md, add ISSUE_GATE.md, a PR template, and richer bug/feature issue templates (state:inbox, impact, scope, acceptance criteria, verification plans).

Automation adds issue-gate.mjs plus a pull_request_target workflow that checks out only the default branch, evaluates PR descriptions against open state:ready issues, and in default observe mode reports results (status, comment, labels) without blocking; block/close modes and cutoff-based legacy exemptions are documented for later activation. Unit tests and a small CI workflow cover the evaluator.

Reviewed by Cursor Bugbot for commit cf6c32e. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The pull request establishes an issue-first contribution process. It updates issue and pull request templates, adds an Issue Gate evaluator with tests, and wires the evaluator into privileged and test GitHub Actions workflows.

Changes

Issue-first contribution flow

Layer / File(s) Summary
Contribution and issue contracts
.github/CONTRIBUTING.md, .github/ISSUE_TEMPLATE/*, .github/PULL_REQUEST_TEMPLATE.md
Contribution guidance now requires one ready issue, an Implements #<issue-number> reference, acceptance-criteria coverage, and verification details. Issue templates collect impact, scope, acceptance criteria, and verification plans.
Issue reference and readiness evaluation
.github/scripts/issue-gate.mjs, .github/scripts/issue-gate.test.mjs
The evaluator parses unique implementation references, applies configured exemptions, validates one open same-repository issue with the ready label, and tests accepted and rejected cases.
Workflow enforcement and reporting
.github/scripts/issue-gate.mjs, .github/workflows/issue-gate.yml, .github/workflows/issue-gate-tests.yml, .github/ISSUE_GATE.md
The workflows run the evaluator from the trusted default branch and in pull request tests. The script updates statuses, labels, and comments, supports observe, block, and close modes, and can close rejected pull requests. Documentation defines pilot configuration and testing.

Sequence Diagram(s)

sequenceDiagram
  participant PullRequest
  participant IssueGateWorkflow
  participant issue_gate_mjs
  participant GitHubAPI
  PullRequest->>IssueGateWorkflow: trigger pull_request_target or manual dispatch
  IssueGateWorkflow->>issue_gate_mjs: run with pull request and gate configuration
  issue_gate_mjs->>GitHubAPI: fetch pull request and referenced issue
  GitHubAPI-->>issue_gate_mjs: return pull request and issue data
  issue_gate_mjs->>issue_gate_mjs: evaluate reference, status, label, and exemptions
  issue_gate_mjs->>GitHubAPI: publish status and synchronize labels/comments
  issue_gate_mjs-->>IssueGateWorkflow: return gate result and exit status
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the issue-first contribution workflow introduced by the documentation, templates, and automation changes.
Description check ✅ Passed The description accurately explains the issue-first workflow, templates, automation, security model, and testing changes.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch lorenze/imp/issue-driven-pull-requests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/PULL_REQUEST_TEMPLATE.md:
- Around line 11-13: Update the Summary prompt in the pull request template to
require authors to explain how the implementation meets the issue’s acceptance
criteria, while retaining the existing requirement to describe the solution and
its alignment with the agreed design.

In @.github/scripts/issue-gate.mjs:
- Around line 30-42: Update the issue-gate evaluation around the reference
parser in .github/scripts/issue-gate.mjs:30-42 to detect Closes, Fixes, and
Resolves references and return a failed evaluation when any are present, while
preserving existing Implements handling. In
.github/scripts/issue-gate.test.mjs:52-60, replace the current “ignores”
assertion with rejection cases covering each prohibited keyword.
- Around line 51-87: Update exemptionReason so cutoff-based legacy pull requests
return a distinct kind from policy:legacy label exemptions, then adjust the
synchronization guard around the label/comment handling near lines 354-364 to
skip synchronization only for the cutoff-specific kind. Preserve the existing
exemption reasons and behavior for policy:legacy, other exemptions, and
non-exempt pull requests.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c338e155-18ad-4ec6-bb08-060df5c228f9

📥 Commits

Reviewing files that changed from the base of the PR and between 7accafb and 66ce0c5.

📒 Files selected for processing (9)
  • .github/CONTRIBUTING.md
  • .github/ISSUE_GATE.md
  • .github/ISSUE_TEMPLATE/bug_report.yml
  • .github/ISSUE_TEMPLATE/feature_request.yml
  • .github/PULL_REQUEST_TEMPLATE.md
  • .github/scripts/issue-gate.mjs
  • .github/scripts/issue-gate.test.mjs
  • .github/workflows/issue-gate-tests.yml
  • .github/workflows/issue-gate.yml

Comment on lines +11 to +13
## Summary

<!-- Explain the solution and how it follows the design agreed in the issue. -->

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Prompt for acceptance-criteria coverage.

The template only asks how the solution follows the agreed design. .github/CONTRIBUTING.md Line 141 also requires an explanation of how the implementation meets the issue's acceptance criteria.

Add this requirement to the summary prompt so the template produces a policy-compliant pull request description.

Proposed change
-<!-- Explain the solution and how it follows the design agreed in the issue. -->
+<!-- Explain the solution, how it follows the agreed design, and how it meets each acceptance criterion. -->
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
## Summary
<!-- Explain the solution and how it follows the design agreed in the issue. -->
## Summary
<!-- Explain the solution, how it follows the agreed design, and how it meets each acceptance criterion. -->
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/PULL_REQUEST_TEMPLATE.md around lines 11 - 13, Update the Summary
prompt in the pull request template to require authors to explain how the
implementation meets the issue’s acceptance criteria, while retaining the
existing requirement to describe the solution and its alignment with the agreed
design.

Comment on lines +30 to +42
const pattern =
/^[\t ]*Implements[\t ]+(?:(?<repository>[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+))?#(?<number>\d+)\b/gim;

for (const match of (body ?? "").matchAll(pattern)) {
const repository = match.groups.repository ?? defaultRepository;
const number = Number.parseInt(match.groups.number, 10);
const key = `${repository.toLowerCase()}#${number}`;

if (!seen.has(key)) {
references.push({ repository, number });
seen.add(key);
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Reject prohibited GitHub closing keywords.

The gate accepts a description that contains both Implements #123 and `Closes `#123. GitHub can then close the referenced issue when the pull request merges. This bypasses the documented state:verification lifecycle.

  • .github/scripts/issue-gate.mjs#L30-L42: detect Closes, Fixes, and Resolves references and return a failed evaluation.
  • .github/scripts/issue-gate.test.mjs#L52-L60: replace the “ignores” assertion with rejection cases for each prohibited keyword.
📍 Affects 2 files
  • .github/scripts/issue-gate.mjs#L30-L42 (this comment)
  • .github/scripts/issue-gate.test.mjs#L52-L60
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/scripts/issue-gate.mjs around lines 30 - 42, Update the issue-gate
evaluation around the reference parser in .github/scripts/issue-gate.mjs:30-42
to detect Closes, Fixes, and Resolves references and return a failed evaluation
when any are present, while preserving existing Implements handling. In
.github/scripts/issue-gate.test.mjs:52-60, replace the current “ignores”
assertion with rejection cases covering each prohibited keyword.

Comment on lines +51 to +87
function exemptionReason(pullRequest, cutoff) {
const labels = labelNames(pullRequest);
for (const label of EXEMPT_LABELS) {
if (labels.has(label)) {
return {
kind: label === "policy:legacy" ? "legacy" : "override",
reason: `the pull request has the \`${label}\` exemption label`,
};
}
}

const actor = pullRequest.user?.login?.toLowerCase();
if (EXEMPT_ACTORS.has(actor)) {
return {
kind: "automation",
reason: `\`${pullRequest.user.login}\` is an exempt automation account`,
};
}

if (cutoff) {
const cutoffTime = Date.parse(cutoff);
if (Number.isNaN(cutoffTime)) {
throw new Error(`ISSUE_GATE_CUTOFF is not a valid date: ${cutoff}`);
}

const createdTime = Date.parse(pullRequest.created_at);
if (Number.isNaN(createdTime)) {
throw new Error(`Pull request has an invalid creation date: ${pullRequest.created_at}`);
}

if (createdTime < cutoffTime) {
return {
kind: "legacy",
reason: `the pull request predates the pilot cutoff (${cutoff})`,
};
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Distinguish cutoff legacy pull requests from policy:legacy exemptions.

exemptionReason assigns legacy to both cases. Lines 355-364 then skip label and comment synchronization for both cases.

If a failed pull request later receives policy:legacy, the needs-ready-issue label and rejection comment remain visible. Return distinct exemption kinds and skip synchronization only for cutoff-based legacy pull requests.

Also applies to: 354-364

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/scripts/issue-gate.mjs around lines 51 - 87, Update exemptionReason
so cutoff-based legacy pull requests return a distinct kind from policy:legacy
label exemptions, then adjust the synchronization guard around the label/comment
handling near lines 354-364 to skip synchronization only for the cutoff-specific
kind. Preserve the existing exemption reasons and behavior for policy:legacy,
other exemptions, and non-exempt pull requests.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit cf6c32e. Configure here.

issueNumber: reference.number,
reason: `issue #${reference.number} does not have the \`${readyLabel}\` label`,
};
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gate rejects in-progress issues

High Severity

evaluatePullRequest only accepts issues labeled state:ready, but the documented issue lifecycle moves work to state:in-progress once implementation starts. After that transition, later synchronize runs fail valid pull requests that still correctly Implements the issue, causing false failures in observe mode and merge blocks once enforcement is enabled.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit cf6c32e. Configure here.

# Until a cutoff is configured, every PR is treated as legacy. This
# keeps the newly merged workflow inert by default.
ISSUE_GATE_CUTOFF: ${{ vars.ISSUE_GATE_CUTOFF || '9999-12-31T00:00:00Z' }}
ISSUE_GATE_MODE: ${{ vars.ISSUE_GATE_MODE || 'observe' }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cutoff safety check bypassed

Medium Severity

The script rejects block/close when ISSUE_GATE_CUTOFF is empty, but the workflow always supplies 9999-12-31T00:00:00Z when the variable is unset. Setting only ISSUE_GATE_MODE to block or close therefore skips the refusal path and treats every PR as legacy-exempt, so enforcement never actually rejects anything.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit cf6c32e. Configure here.

@theCyberTech theCyberTech left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Might be worth revisiting the CodeRabbit reviews in the future as quick wins

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants