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