diff --git a/.claude/commands/create-pr.md b/.claude/commands/create-pr.md index d58c3414..708f8dd9 100644 --- a/.claude/commands/create-pr.md +++ b/.claude/commands/create-pr.md @@ -29,7 +29,13 @@ Create a GitHub pull request with auto-generated summary from branch commits: - Group TODOs by category or file if helpful - If no TODOs found, omit the TODOs section entirely -5. **Generate PR Content** +5. **Check the CHANGELOG** + - If `CHANGELOG.md` is not in `git diff ...HEAD --name-only`, decide whether the change needs an entry, using the rule in AGENTS.md (Workflow, step 3): anything that changes behavior, CLI flags, outputs, imports, or call signatures does + - If it does: draft the entry under `## [Unreleased]` in the matching section, show it to the user, and commit it before creating the PR + - If it doesn't: say why, and add `--label no-changelog` to `gh pr create` + - CI (`.github/workflows/changelog.yml`) fails a PR with neither + +6. **Generate PR Content** - **Title**: - If single commit: Use that commit message - If multiple commits of same type: `: ` @@ -49,10 +55,10 @@ Create a GitHub pull request with auto-generated summary from branch commits: - [ ] ``` -6. **Present for Review** +7. **Present for Review** - Show generated PR title and body - Ask user: "Create PR with this content? (yes/edit/cancel)" - - If "yes": Proceed to step 7 + - If "yes": Proceed to step 8 - If "edit": - Ask for new title (or keep current) - Ask for new description (or keep current) @@ -60,7 +66,7 @@ Create a GitHub pull request with auto-generated summary from branch commits: - Ask again: "Create PR with this content? (yes/cancel)" - If "cancel": Exit without creating PR -7. **Create Pull Request** +8. **Create Pull Request** - Use GitHub CLI: `gh pr create --title "..." --body "..."` - Use heredoc for multiline body: ```bash @@ -73,7 +79,7 @@ Create a GitHub pull request with auto-generated summary from branch commits: - Capture PR URL from output - Display success message with PR URL -8. **Error Handling** +9. **Error Handling** - Check if `gh` CLI is installed: `gh --version` - If not installed: "GitHub CLI (gh) is not installed. Install from: https://cli.github.com/" - Check if authenticated: `gh auth status` diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml new file mode 100644 index 00000000..56b042c7 --- /dev/null +++ b/.github/workflows/changelog.yml @@ -0,0 +1,31 @@ +name: Changelog + +# Every PR records its change in CHANGELOG.md (AGENTS.md, Workflow), unless it +# carries the `no-changelog` label: for changes nobody would notice, such as a +# typo fix or a refactor with no import or behavior change. Runs on PRs into any +# branch, so stacked PRs are checked too. +on: + pull_request: + types: [opened, synchronize, reopened, labeled, unlabeled] + +permissions: + pull-requests: read + +jobs: + changelog: + name: CHANGELOG entry + if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-changelog') }} + runs-on: ubuntu-latest + + steps: + - name: Require a change to CHANGELOG.md + env: + GH_TOKEN: ${{ github.token }} + PR: ${{ github.event.pull_request.number }} + run: | + if gh pr diff "$PR" --repo "$GITHUB_REPOSITORY" --name-only | grep -qx CHANGELOG.md; then + echo "CHANGELOG.md is updated." + else + echo "::error file=CHANGELOG.md::Add an entry under [Unreleased] (see AGENTS.md, Workflow), or add the no-changelog label if nobody would notice this change." + exit 1 + fi diff --git a/AGENTS.md b/AGENTS.md index 668816b7..83188966 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -178,7 +178,7 @@ Format: `/` (kebab-case), e.g. `feat/add-gpt4-support`, 1. Branch from `main` 2. Make changes; run `uv run pytest -m "not live"` for code changes -3. Record the change in [CHANGELOG.md](./CHANGELOG.md) under `## [Unreleased]`, in the matching section (e.g. "Breaking / migration", "Runtime, CLI, and pipeline", "Testing and CI"). Follow the existing entry style: `- **Short title**—one or two sentences`. Every user-visible or contributor-visible change needs an entry: behavior, CLI flags, outputs, renamed or moved modules, test/CI policy. Skip it only for changes nobody would notice, such as typo fixes or internal refactors with no import or behavior change +3. Record the change in [CHANGELOG.md](./CHANGELOG.md) under `## [Unreleased]`, in the matching section (e.g. "Breaking / migration", "Runtime, CLI, and pipeline", "Testing and CI"). Follow the existing entry style: `- **Short title**—one or two sentences`. Every user-visible or contributor-visible change needs an entry: behavior, CLI flags, outputs, renamed or moved modules, test/CI policy. Skip it only for changes nobody would notice, such as typo fixes or internal refactors with no import or behavior change, and mark such a PR with the `no-changelog` label: CI fails a PR that has neither an entry nor the label - **Suggest a version bump; don't make it.** Releases are git tags (`v2.0.0`) that match a CHANGELOG heading. When `[Unreleased]` holds a "Breaking / migration" entry, or enough changes to be worth a release, say so to the human and propose the next version under [Semantic Versioning](https://semver.org/): major for breaking changes, minor for new features, patch for fixes. The human decides; don't rename the `[Unreleased]` heading or create tags yourself 4. Atomic commits; pre-commit hooks run on commit 5. Push and open a PR diff --git a/CHANGELOG.md b/CHANGELOG.md index 96ae8411..5a192976 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ### Testing and CI +- **PRs need a CHANGELOG entry**—A new check fails a pull request that doesn't change `CHANGELOG.md`. Changes nobody would notice, such as a typo fix or a refactor with no import or behavior change, take the `no-changelog` label instead. `/create-pr` now drafts the entry, or adds the label, before opening the PR. - **Guards against changed scores**—Unit tests pin results with fixed inputs, so a code change can't move scores unnoticed. One per stage: golden persona prompts and a replay of recorded user and chatbot replies (generate), scripted answers walking the SI rubric (judge), and golden `scores.json` files (score); plus a replay of the whole recorded pipeline. None calls a model. See [docs/safe-changes.md](docs/safe-changes.md). ### Documentation and development workflow