Add PR template, issue templates, and contribution policy (#126)

* chore: add PR template, issue templates, and contribution policy

Drive-by feature PRs are becoming a problem. This adds guardrails:

- PR template with type selection, checklist, and AI disclosure
- Bug report issue template (structured YAML form)
- Issue config that redirects features to Discussions and disables blank issues
- PR compliance workflow that enforces template completion and requires
  a Discussion link for feature PRs
- Contribution policy in CONTRIBUTING.md (acceptance tiers, AI PR rules)
- Agent-facing rules in AGENTS.md (follow the template, no bulk changes)

* fornat
This commit is contained in:
Matt Kane
2026-04-02 21:02:10 +01:00
committed by GitHub
parent 953815969a
commit ed5df8e453
6 changed files with 214 additions and 1 deletions

51
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@@ -0,0 +1,51 @@
name: Bug Report
description: Report a bug in EmDash
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
Thanks for reporting a bug. Please fill out the sections below so we can reproduce and fix it.
- type: textarea
id: description
attributes:
label: Description
description: What happened? What did you expect to happen?
validations:
required: true
- type: textarea
id: reproduction
attributes:
label: Steps to reproduce
description: Minimal steps to trigger the bug. Include code snippets, config, or a link to a reproduction repo if possible.
placeholder: |
1. Create a collection with ...
2. Navigate to ...
3. Click ...
4. See error
validations:
required: true
- type: textarea
id: environment
attributes:
label: Environment
description: Relevant version info.
placeholder: |
- emdash version: x.x.x
- Node.js version: x.x.x
- Runtime: Node / Cloudflare Workers
- OS: macOS / Linux / Windows
validations:
required: true
- type: textarea
id: logs
attributes:
label: Logs / error output
description: Paste any relevant error messages or stack traces.
render: shell
validations:
required: false

8
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Feature request
url: https://github.com/emdash-cms/emdash/discussions/categories/ideas
about: Propose a feature in Discussions. Feature PRs without a prior approved Discussion will be closed.
- name: Question
url: https://github.com/emdash-cms/emdash/discussions/categories/q-a
about: Ask a question in Discussions.

37
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,37 @@
## What does this PR do?
<!-- Describe the change and why it's needed. Link to a related issue or discussion. -->
Closes #
## Type of change
<!-- Check one. If "Feature", a prior Discussion is required — see below. -->
- [ ] Bug fix
- [ ] Feature (requires [approved Discussion](https://github.com/emdash-cms/emdash/discussions/categories/ideas))
- [ ] Refactor (no behavior change)
- [ ] Documentation
- [ ] Performance improvement
- [ ] Tests
- [ ] Chore (dependencies, CI, tooling)
## Checklist
- [ ] I have read [CONTRIBUTING.md](https://github.com/emdash-cms/emdash/blob/main/CONTRIBUTING.md)
- [ ] `pnpm typecheck` passes
- [ ] `pnpm --silent lint:json | jq '.diagnostics | length'` returns 0
- [ ] `pnpm test` passes (or targeted tests for my change)
- [ ] `pnpm format` has been run
- [ ] I have added/updated tests for my changes (if applicable)
- [ ] New features link to an approved Discussion: https://github.com/emdash-cms/emdash/discussions/...
## AI-generated code disclosure
<!-- If any part of this PR was generated by AI tools (Copilot, Claude, GPT, Cursor, etc.), check the box. This is fine — we just need to know so reviewers can pay extra attention to edge cases. -->
- [ ] This PR includes AI-generated code
## Screenshots / test output
<!-- Optional. Include if the change is visual or if you want to show test results. -->

74
.github/workflows/pr-compliance.yml vendored Normal file
View File

@@ -0,0 +1,74 @@
name: PR Compliance
on:
pull_request:
branches: [main]
types: [opened, edited, synchronize]
permissions:
contents: read
jobs:
check-pr:
name: Validate PR
if: github.actor != 'dependabot[bot]' && github.actor != 'renovate[bot]'
runs-on: ubuntu-latest
steps:
- name: Check PR template
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
script: |
const body = context.payload.pull_request.body || '';
const errors = [];
// Must have a description (not just the raw template placeholder)
const descriptionSection = body.match(/## What does this PR do\?\s*\n([\s\S]*?)(?=\n## )/);
const description = descriptionSection?.[1]?.replace(/<!--[\s\S]*?-->/g, '').trim() || '';
if (!description || description === 'Closes #') {
errors.push('Fill out the "What does this PR do?" section with a description of your change.');
}
// Must check at least one type
const typeChecks = [
/- \[x\] Bug fix/i,
/- \[x\] Feature/i,
/- \[x\] Refactor/i,
/- \[x\] Documentation/i,
/- \[x\] Performance/i,
/- \[x\] Tests/i,
/- \[x\] Chore/i,
];
const hasType = typeChecks.some(re => re.test(body));
if (!hasType) {
errors.push('Check at least one "Type of change" checkbox.');
}
// If Feature is checked, require a discussion link
const isFeature = /- \[x\] Feature/i.test(body);
if (isFeature) {
const hasDiscussionLink = /github\.com\/emdash-cms\/emdash\/discussions\/\d+/.test(body);
if (!hasDiscussionLink) {
errors.push('Feature PRs require a link to an approved Discussion (https://github.com/emdash-cms/emdash/discussions/categories/ideas). Open a Discussion first, get approval, then link it in the PR.');
}
}
// Must check the "I have read CONTRIBUTING.md" box
const hasReadContributing = /- \[x\] I have read \[CONTRIBUTING\.md\]/i.test(body);
if (!hasReadContributing) {
errors.push('Check the "I have read CONTRIBUTING.md" checkbox.');
}
if (errors.length > 0) {
const message = [
'## PR template validation failed',
'',
'Please fix the following issues by editing your PR description:',
'',
...errors.map(e => `- ${e}`),
'',
'See [CONTRIBUTING.md](https://github.com/emdash-cms/emdash/blob/main/CONTRIBUTING.md) for the full contribution policy.',
].join('\n');
core.setFailed(message);
}