Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions .github/workflows/pr-title.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Validate that every PR title is a Conventional Commit.
#
# We squash-merge with the PR title as the commit subject (repo setting
# squash_merge_commit_title=PR_TITLE), and release-please derives the version
# bump + changelog from that subject. So a non-conventional PR title would
# silently break versioning — this check is the guard.
#
# pull_request_target is used (not pull_request) so the check still runs for
# fork PRs; it only reads the title via the API and never checks out PR code,
# so there is no untrusted-code execution risk.
name: pr-title

on:
pull_request_target:
types: [opened, edited, reopened, synchronize]

permissions:
pull-requests: read

concurrency:
group: pr-title-${{ github.event.pull_request.number }}
cancel-in-progress: true

jobs:
validate:
name: conventional PR title
runs-on: ubuntu-latest
steps:
- uses: amannn/action-semantic-pull-request@v6.1.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
# Keep in sync with release-please's understanding of types.
types: |
feat
fix
docs
chore
ci
refactor
perf
test
build
revert
# Subject must not be empty and not start with an uppercase letter.
subjectPattern: ^(?![A-Z]).+$
subjectPatternError: |
The subject "{subject}" must start with a lowercase letter, e.g.
"fix: correct the origin resolver", not "fix: Correct ...".
Comment thread
so0k marked this conversation as resolved.
2 changes: 2 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ Features follow SpecLedger: **Specify → Clarify → Plan → Tasks → Review

**Commit & PR conventions.** Conventional prefixes (`feat:`, `fix:`, `chore:`, `docs:`), imperative subjects ≤72 chars, scoped to the feature (e.g. `docs(002): …`). Reference related issues in the body; call out migrations / new binaries explicitly. PRs carry a concise summary + testing evidence (`make test-unit`, `make test-integration`) and a CLI transcript when behavior changes.

**PR titles are load-bearing.** The repo is **squash-merge only**, and the squash commit subject is the **PR title** (GitHub setting `squash_merge_commit_title=PR_TITLE`). `release-please` derives the version bump + changelog from that subject, so **every PR title must be a Conventional Commit** — enforced by the `pr-title` workflow (`.github/workflows/pr-title.yml`). Only `fix:` (→ patch), `feat:` (→ minor), and `!`/`BREAKING CHANGE` (→ major while ≥1.0.0) cut a release; `docs:`/`chore:`/`ci:`/`refactor:`/`test:`/`build:` land without a release. Don't dress a non-functional change as `fix:`/`feat:` to force a release.

**Work-item tracking.** The durable, team-visible record lives in the SpecLedger issue tracker — `sl issue`, stored per-spec in `specledger/<spec>/issues.jsonl` (committed to git). The agent's in-session task list (the `Task*` tools) is an ephemeral execution aid, not a substitute for that committed record.

<!-- >>> specledger-generated -->
Expand Down