We use Conventional Commits (angular preset):
<type>(<scope>): <subject>
- Subject: imperative, ≤50 chars ideal (hard limit ~72), no trailing period.
- Body: English only when needed, ~72 chars wrap.
- Footer:
BREAKING CHANGE:for incompatible changes; issue refs (e.g.Closes #12). - Do not create release commits manually — release PRs are generated by tooling.
- feat — new public capability (API/behavior)
- fix — bug fix affecting public behavior
- refactor — internal changes, no API/behavior change
- perf — performance improvements
- docs — README / phpdoc / examples
- test — tests only
- build — packaging/composer/build pipeline
- ci — CI configs and workflows
- chore — deps and repo housekeeping
- revert — revert a previous commit
- core — shared code / interfaces
- gzip —
GzipCompressor - brotli —
BrotliCompressor - file —
FileCompressor - negotiator —
EncodingNegotiator - policy —
DefaultCompressionPolicy/ rules - result —
CompressionResult - tests — unit/integration
- docs — README, examples
- ci-config — GitHub Actions
- repo — meta (editorconfig, gitignore, license)
Prefer core when unsure. Use ! for breaking (e.g. feat!: drop PHP 8.3 support).
feat(core): deterministic gzip headersfix(brotli): throw on invalid levelrefactor(file): atomically persist permissionsdocs(readme): add nginx static configci(ci-config): add commitlint job
When asked to produce a commit message: output ONLY the Conventional Commit text. No extra prose/markdown.