From 9569fb84fab4c0c503661538e48d9c990f725217 Mon Sep 17 00:00:00 2001 From: jamubc <150970140+jamubc@users.noreply.github.com> Date: Sat, 13 Jun 2026 00:41:35 -0700 Subject: [PATCH] ci: scope OpenSpec validation to feature changes Docs, chore, fix, ci, test, refactor, style, build, and perf PRs and issues carry no requirement delta, so they no longer need the OpenSpec proposal sections. The validator now runs its required-section checks only on feature changes (a feat: title or feat/ branch, or an issue labeled feature/enhancement) and clears any stale needs-info label on everything else. Update CONTRIBUTING and the PR template to match. --- .github/PULL_REQUEST_TEMPLATE.md | 46 +++++++++++++++++-------- .github/workflows/validate-openspec.yml | 44 +++++++++++++++++------ CONTRIBUTING.md | 38 ++++++++++++++------ 3 files changed, 92 insertions(+), 36 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 8e2b530..b59942d 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,34 +1,50 @@ - -## Why +## Summary - + -## What Changes +## Changes - + - +## Verification + + + +- [ ] 1.1 +- [ ] 1.2 + + +State the new or changed behavior in RFC 2119 language (SHALL, MUST, SHOULD, MAY). +Include "(Previously: ...)" notes for changed behavior. Use ADDED Requirements for +new behavior and REMOVED Requirements, with rationale, for removed behavior. ### Requirement: ## Scenario - - GIVEN WHEN THEN -## Verification - - - -- [ ] 1.1 -- [ ] 1.2 +──────────────────────────────────────────────────────────────────────────── --> diff --git a/.github/workflows/validate-openspec.yml b/.github/workflows/validate-openspec.yml index eb278bb..4833d48 100644 --- a/.github/workflows/validate-openspec.yml +++ b/.github/workflows/validate-openspec.yml @@ -30,6 +30,37 @@ jobs: return; } const body = target.body || ''; + const title = (target.title || '').trim(); + const branch = isPR ? ((context.payload.pull_request.head || {}).ref || '') : ''; + + const issue_number = target.number; + const { owner, repo } = context.repo; + const labels = (target.labels || []).map(l => typeof l === 'string' ? l : l.name); + + async function clearNeedsInfo() { + if (labels.includes(label)) { + await github.rest.issues.removeLabel({ owner, repo, issue_number, name: label }) + .catch(e => core.warning(`Could not remove label: ${e.message}`)); + } + } + + // The OpenSpec proposal format only applies to changes that add or + // alter requirements, i.e. features. Docs, chore, fix, ci, test, + // refactor, style, build, and perf changes carry no requirement + // delta and are exempt, as are bug reports and other non-feature + // issues. A target is treated as a feature when its title uses the + // conventional `feat:` prefix or its branch starts with `feat/`. + const isFeature = + /^feat(\([^)]*\))?!?:/i.test(title) || + /^feat\//i.test(branch) || + labels.includes('feature') || + labels.includes('enhancement'); + + if (!isFeature) { + await clearNeedsInfo(); + core.info('Not a feature change; OpenSpec proposal format not required.'); + return; + } const required = [ { name: 'Why', pattern: /^#{1,3}\s*Why\b|^\*{2}Why\*{2}/im }, @@ -41,16 +72,9 @@ jobs: ]; const missing = required.filter(r => !r.pattern.test(body)).map(r => r.name); - const issue_number = target.number; - const { owner, repo } = context.repo; - - const labels = (target.labels || []).map(l => typeof l === 'string' ? l : l.name); if (missing.length === 0) { - if (labels.includes(label)) { - await github.rest.issues.removeLabel({ owner, repo, issue_number, name: label }) - .catch(e => core.warning(`Could not remove label: ${e.message}`)); - } + await clearNeedsInfo(); core.info('All OpenSpec sections present.'); return; } @@ -58,11 +82,11 @@ jobs: const comment = [ `### OpenSpec format check failed`, ``, - `This ${isPR ? 'pull request' : 'issue'} body is missing required OpenSpec sections:`, + `This feature ${isPR ? 'pull request' : 'issue'} is missing required OpenSpec sections:`, ``, ...missing.map(m => `- [ ] **${m}**`), ``, - `Please edit the body to include them. See the [contributing guide](https://github.com/Jam-Sw/.github/blob/main/CONTRIBUTING.md) for the format.`, + `Feature changes must propose the new behavior formally. Non-feature changes (docs, chore, fix, ci, test, refactor, style, build, perf) are exempt and need no spec sections. See the [contributing guide](https://github.com/Jam-Sw/.github/blob/main/CONTRIBUTING.md) for the format.`, ].join('\n'); // Forked-PR tokens are read-only; fall back to failing the check. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1041966..a0edc26 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,13 +1,27 @@ # Contributing to Jam&Sw -Every issue and pull request in the Jam&Sw organization follows the -**OpenSpec format**. It makes changes reviewable, testable, and honest: a -reader should be able to understand why a change exists, what the correct -behavior is, and exactly how to verify it: without reading the diff first. +Feature work in the Jam&Sw organization follows the **OpenSpec format**. It +makes changes reviewable, testable, and honest: a reader should be able to +understand why a change exists, what the correct behavior is, and exactly how +to verify it, without reading the diff first. + +## When the OpenSpec format is required + +The format applies to **feature changes**: anything that adds or alters a +requirement or user-visible behavior. A change is treated as a feature when +its title uses the conventional `feat:` prefix or its branch starts with +`feat/`. + +Everything else is exempt and needs no spec sections: `docs:`, `chore:`, +`fix:`, `ci:`, `test:`, `refactor:`, `style:`, `build:`, and `perf:` changes, +along with bug reports and other non-feature issues. Write a plain Summary / +Changes / Verification body for those. Forcing requirement language and +GIVEN/WHEN/THEN scenarios onto a docs typo or a dependency bump adds noise, +not clarity. ## The OpenSpec format -Every issue and PR body contains these sections, in order: +Every **feature** issue and PR body contains these sections, in order: ### 1. Why @@ -70,14 +84,16 @@ SHALL…", never "It should…"). ## Enforcement -- Issue forms in this repository require every OpenSpec section, and blank - issues are disabled organization-wide. - The reusable workflow [`validate-openspec.yml`](.github/workflows/validate-openspec.yml) checks - issue and PR bodies for the required sections. Repositories opt in with the - two-line caller in [`examples/validate.yml`](examples/validate.yml). -- Bodies missing sections get a `needs-info` label and a comment listing - what's missing; the label is removed automatically once the body is fixed. + feature issue and PR bodies for the required sections. Repositories opt in + with the two-line caller in [`examples/validate.yml`](examples/validate.yml). +- The workflow only runs its checks on feature changes (title `feat:` or + branch `feat/`, or an issue labeled `feature`/`enhancement`). Non-feature + changes pass automatically. +- Feature bodies missing sections get a `needs-info` label and a comment + listing what's missing; the label is removed automatically once the body is + fixed, or once the change is reclassified as non-feature. ## Repository-local overrides