Conversation
Routing only ever sees the PR title. The repo squash-merges, so that title becomes the release commit — a branch whose commits carry a higher release class than its title walks straight past the routing guard. The guard fails a PR that mixes `feat:` and `fix:`, hides a `feat:` or a breaking commit under a `fix:` title, carries a releasing commit under a non-releasing title, or spreads releasing commits over several scopes. Repeats within one scope pass. Non-releasing commits mix with anything, an unparsable subject (`wip`, `fixup!`) is ignored, and promotion/sync heads are exempt — they carry every class by design (ADR 0004 §8). The last case needs more than the subjects, so the job runs the release gate's own path classifier and fails when the paths reach a consumer while the title's type does not. Checked in that direction only: a `fix(docs):` title over paths that reach nobody costs nothing. Shape follows the existing guards — pure lib, IO shell, self-test the job runs first. Over `main` since #2933 the path half fires on 7 of 81 conventional merges. Reviewed as #3102, which was merged into the #3098 branch rather than into main; this pulls it back out onto its own line. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Contributor
Coverage Report for ./packages/components/
File CoverageNo changed files found. |
Contributor
🚀 Preview DeploymentPreview environments are ready:
Images:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Routing only ever sees the PR title. The repo squash-merges, so that title becomes the release commit — which means a branch whose commits carry a higher release class than its title walks straight past the routing guard.
What it catches
Everything below is information the squash merge destroys, because only the title survives.
feat:andfix:commits in one PRfeat:commit under afix:titlefix:tomain, so the feature lands on the stable line!or aBREAKING CHANGE:footer) under afix:/feat:titledocs/chore, and the commit's own description is discardedfix(List):plusfix(Button):keeps one subject, so the other fix reaches no changelogfeat:title over nothing butfix:commitsdocs:commit, where no commit subject saysfixat all — #2902 changed two.module.scssfiles underdocs: set max text widthRepeats within one scope are fine:
fix(List): aplusfix(List): bis one logical change, and the surviving subject still names it.What it deliberately allows
chore/ci/refactor/stylealongside either.wip,review feedback,fixup! …and merge commits are normal on a branch; failing on them would make this a nuisance rather than a gate. An unknown type (wip(List): …) counts as unparsable, not as non-releasing.fix:. Whether that releases anything is a different question — the path-based release gate answers it.next,x.x,sync/*,release/*): they carry every class by design (ADR 0004 §8).The paths, in one direction
The last row needs more than the commit subjects, so the job runs the release gate's own classifier and fails when the changed paths reach a consumer while the title's type does not. The remedy is a releasing type with the same scope —
fix(docs): add a migration entrysays both things at once: it ships, and it is documentation. No tag, no second vocabulary.Checked in one direction only. A
fix(docs):title over paths that reach nobody costs nothing — the paths decide whether anything publishes, andfix(docs):for a docs-app fix is honest (#3020, #2993). Dependabot's generated*(deps)titles are exempt: a group is not homogeneous in effect andcommit-message.prefixis per ecosystem, so no title it can produce would be true for all of it.Over
mainsince #2933 this half fires on 7 of 81 conventional merges — #2902, #3016 and #3062 (genuinely mislabelled), #3000 (a build config plus a dependency patch undertest:), #2954 (honest docs that shipscomponents/AGENTS.md, so it wantsfix(docs):), and #2929 and #3013 where the classifier stays deliberately fail-safe.Shape
Follows the existing guard pattern in this repo — a pure lib, an IO shell, and a self-test the job runs before it:
.github/scripts/commit-mixture-lib.mjs—classifyCommitandclassifyMixture, no git, no IO.github/scripts/commit-mixture-guard.mjs— readsgit log -z --format=%s%n%b, writes::error::per offending commit.github/scripts/commit-mixture-lib.test.mjs— 16 testscommit-mixturejob incommit-guard.yml, gated to PRs againstmain/next-zrather than a textual record delimiter, because a commit body can contain any text but not a NUL.Verified
All 16 lib tests plus the real shapes through the guard end to end:
feat+fixmixed fails naming both commits, afeatunder afixtitle fails as smuggling, aBREAKING CHANGE:footer under afixtitle fails, afixunder adocstitle fails naming the changelog consequence, two fixes in different scopes fail naming the scopes, two fixes in the same scope pass,feat+docs+wippasses, and a docs-only branch titledfix(docs):passes.Merge after #3098
This was reviewed as #3102 and merged into the #3098 branch rather than into
main; #3098 has now been pulled apart, and this is its own line, rebased onto currentmain.It still depends on #3098, which sharpens the path classifier this guard runs:
files, andDockerfile/.dockerignorein the package-local denylist — without those, the path half would fire on test(remote-react-components): extract the scenario-focus wait into a documented lib #3009 and ci: build the preview apps in CI and let the image only package them #3008, which reach nobody;MANIFEST_SNAPSHOT_DIRprotocol theClassify the changed pathsstep uses. Today's classifier readsROOT_MANIFEST_*instead and ignores the snapshot dir, so until fix(ci): stop publishing on paths that cannot reach a consumer #3098 lands the manifest key-diff refinement is inert here and the classification stays conservative.Independent of #3137 (the changelog preset) — no shared files.
🤖 Generated with Claude Code