diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..aa4bef1 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,27 @@ + + +## What changed + + + +## How it was verified + + + +--- + +**Before this can merge** (both are enforced, not suggestions): + +- [ ] Exactly one `type:` label is applied. The `pr-type-label` check fails without it. + - `type:feature` — new capability or user-visible behaviour + - `type:bugfix` — corrects behaviour that was already meant to work + - `type:hotfix` — urgent production fix, expedited + - `type:chore` — dependencies, tooling, refactors, config, release plumbing + - `type:docs` — documentation, runbooks, changelog + - `type:security` — vulnerability fix, hardening, secret rotation +- [ ] Approved by someone other than the author. Self-merge is blocked for everyone, admins included. + +These two gates are Redpine's SOC2 change-management control. If one is blocking something genuinely urgent, get a second person to approve — do not ask for the control to be disabled. diff --git a/.github/workflows/pr-type-label.yml b/.github/workflows/pr-type-label.yml new file mode 100644 index 0000000..854c48b --- /dev/null +++ b/.github/workflows/pr-type-label.yml @@ -0,0 +1,62 @@ +# Canonical source: redpine-ai/redpine-cc-plugin +# plugins/redpine-eng/skills/redpine-pr-standard/assets/pr-type-label.yml +# Re-sync with: scripts/sync-pr-standard.sh +# +# SOC2 change-management control. `pr-type-label` is a REQUIRED status check on +# main in every Redpine repo. Never add a paths or paths-ignore filter to this +# workflow, and never let a CI-scoping script skip it: a required check that does +# not run leaves every PR unmergeable forever. +name: PR type label + +on: + pull_request: + types: [opened, edited, reopened, synchronize, labeled, unlabeled, ready_for_review] + +permissions: + contents: read + +concurrency: + group: pr-type-label-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + # Job name is the required-status-check context. Do not rename it. + pr-type-label: + name: pr-type-label + runs-on: ubuntu-latest + steps: + - name: Require exactly one type label + env: + # Read through env, never interpolated into the shell, so a label + # containing shell metacharacters cannot inject into this script. + PR_LABELS: ${{ toJSON(github.event.pull_request.labels.*.name) }} + run: | + set -euo pipefail + + ALLOWED="type:feature type:bugfix type:hotfix type:chore type:docs type:security" + + types=$(printf '%s' "$PR_LABELS" | jq -r '.[] | select(startswith("type:"))') + count=$(printf '%s' "$types" | grep -c . || true) + + if [ "$count" -eq 0 ]; then + echo "::error::This PR has no type label. Add exactly one of: $ALLOWED" + exit 1 + fi + + if [ "$count" -gt 1 ]; then + echo "::error::This PR has $count type labels ($(printf '%s' "$types" | tr '\n' ' ')). Exactly one is allowed." + exit 1 + fi + + # Exact match against each allowed value. A substring test would accept a + # single label literally named "type:feature type:bugfix". + ok=0 + for a in $ALLOWED; do + if [ "$types" = "$a" ]; then ok=1; break; fi + done + if [ "$ok" -ne 1 ]; then + echo "::error::'$types' is not a valid type label. Use one of: $ALLOWED" + exit 1 + fi + + echo "OK: $types" diff --git a/AGENTS.md b/AGENTS.md index c3dfdef..cee987f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -83,3 +83,29 @@ See `CONTRIBUTING.md` for build/test/lint commands and the PR checklist. blocks would be silently dropped. - Assisted search and the quota endpoint are REST-only on the server; the CLI has no MCP route to them. + + +## Pull requests + +Two gates are enforced on the default branch and are not optional. They are +Redpine's SOC2 change-management control. + +1. **Every PR carries exactly one `type:` label.** The `pr-type-label` status check + fails without it, and the PR cannot merge. Apply the label when you open the PR, + not afterwards: + + | Label | Use for | + |---|---| + | `type:feature` | New capability or user-visible behaviour | + | `type:bugfix` | Corrects behaviour that was already meant to work | + | `type:hotfix` | Urgent production fix, expedited | + | `type:chore` | Dependencies, tooling, refactors, config, release plumbing | + | `type:docs` | Documentation, runbooks, changelog | + | `type:security` | Vulnerability fix, hardening, secret rotation | + +2. **Every PR is approved by someone other than its author.** Self-merge is blocked + for everyone, repository admins included. An automated review is useful evidence + but does not satisfy this gate; a human other than the author must approve. + +If you are an agent opening a PR: apply the `type:` label in the same command that +creates the PR (`gh pr create --label type:...`), and never merge your own PR.