Skip to content

chore(changelog): one file per entry in changelog.d/ — merges stop conflicting - #196

Merged
MrChengLen merged 2 commits into
mainfrom
pr-changelog-fragments
Oct 6, 2026
Merged

MrChengLen merged 2 commits into
mainfrom
pr-changelog-fragments

Conversation

@MrChengLen

Copy link
Copy Markdown
Owner

Every PR added its changelog entry directly under ## [Unreleased], on the same line each time. So every merge made all other open PRs CONFLICTING, and GitHub runs no pull_request workflows on a conflicting PR.

Since 2026-09-01:

  • 47 of 72 merged PRs touched CHANGELOG.md.
  • 22 needed the three-commit take-out / merge / put-back dance, which added 66 commits on main.
  • More than half of all PR CI runs were cancelled by the re-pushes.

How it works now

New entry One file per change: changelog.d/<YYYY-MM-DD>-<topic>.md, in the house style (first line ### Fixed — headline, then paragraphs). New files never conflict.
Fold python scripts/changelog.py moves the files into CHANGELOG.md below the pointer, newest first, and deletes them.
Release python scripts/changelog.py --release X.Y.Z in the release-prep PR also cuts ## [X.Y.Z] — <date> (docs/development.md, steps 1 and 3). If a PR merges late, running the same command again adds its entry under the heading already cut. An older or empty release is refused.
Guard tests/test_changelog_fragments.py, inside the required lint-and-test. It checks names and first lines, and fails with the fix spelled out when an entry lands at the top of CHANGELOG.md. It also tests fold, release and refusal on temp files.

The 79 entries already under [Unreleased] stay where they are and move under the next version at release.

Not chosen

  • merge=union in .gitattributes. GitHub's server-side merges (merge button, mergeability, update-branch) don't apply repository merge drivers, and git itself stopped reading attributes from HEAD in bare repositories (git 51441e6). It would also never run for API commits.
  • Generated release notes only. release.yml already attaches GitHub's PR-title list. As a replacement, it drops most of the content (rationale, operator actions) and the copy inside the signed tarball.
  • towncrier / scriv. Their naming and grouping differ from the house style, and they add a dependency for about 50 lines of logic.

Notes

  • Unlike CHANGELOG.md, fragments go through the hooks' and the scope guard's content scans. That is the safer direction; changelog.d/README.md says to describe ops-only names instead of spelling them out.
  • An open PR that still carries a CHANGELOG.md entry converts with a single commit: set CHANGELOG.md back to its merge-base bytes and add the entry, unchanged, as a fragment.
  • The local agent instructions (CLAUDE.md, the ship skill) switch to fragments as soon as this merges. They are not part of the repo.

Verification

  • Local: full suite 1667 passed / 80 skipped; ruff clean; gitleaks and the pre-commit scope guard clean.
  • Security and code review (commit-review gate) done. Their findings are in: a late-merge re-run, refusal of links and non-fragment files, BOM/CRLF handling, clearer guard messages, and changelog.d/ paths pinned as scanned in test_hook_allowlist_regression.py.
  • Release dry-run on a copy of main's CHANGELOG.md: the fragment lands under a new ## [1.2.0] — <date> above the existing [Unreleased] entries, and the pointer stays.

🤖 Generated with Claude Code

…nflicting

Every PR inserted its entry directly under `## [Unreleased]`, the same line
each time, so each merge left every other open PR CONFLICTING, and GitHub
runs no pull_request workflows on a conflicting PR. Since 2026-09-01, 47 of 72
merged PRs touched CHANGELOG.md. 22 of them needed the take-out / merge /
put-back dance (66 extra commits), and 54-58% of PR CI runs were cancelled by
the re-pushes.

New entries go in changelog.d/<YYYY-MM-DD>-<topic>.md, one each, in the house
style (first line `### Type — headline`). scripts/changelog.py folds them in
newest first below a pointer under [Unreleased]. `--release X.Y.Z` also cuts
the version heading in the release-prep PR. docs/development.md step 1 now
runs it, and a new step checks changelog.d/ is empty before tagging.
tests/test_changelog_fragments.py checks fragment names and first lines. It
fails, saying what to do instead, when an entry is inserted at the top of
CHANGELOG.md, and it covers fold, release and refusal on temp files.
Re-running `--release X.Y.Z` after a late merge adds the entry under the
heading already cut. An older or empty release is refused. Anything in
changelog.d/ that isn't a regular YYYY-MM-DD-<topic>.md file is refused too
(links, other extensions); a BOM or CRLF from Windows editors is normalised.

Not chosen:
- .gitattributes `merge=union`: GitHub's server-side merges (merge button,
  mergeability, update-branch) don't apply repository merge drivers. git
  reverted reading attributes from HEAD in bare repos (51441e6). It would
  not run for API commits either.
- Generated release notes only: release.yml already attaches GitHub's
  PR-title list. As a replacement it loses most of the content (rationale,
  operator actions) and the copy inside the signed tarball.
- towncrier / scriv: they name and group entries differently from the house
  style, and add a dependency for about 50 lines of logic.

The existing [Unreleased] entries stay where they are and move under the next
version at release. Fragments go through the hooks' and scope guard's content
scans (CHANGELOG.md is exempt), which is the safer direction.

Verified: full suite 1667 passed / 80 skipped; a release fold dry-run
on a copy of main's CHANGELOG; ruff check + format clean; gitleaks and the
pre-commit scope guard clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MrChengLen added a commit that referenced this pull request Oct 6, 2026
…HS256-only

pip-audit started failing the required lint-and-test job on every PR:
python-jose 3.5.0 is affected by CVE-2026-85394 (GHSA-3qf3-8w2g-rqmx,
CRITICAL / CVSS 9.1). It is an incomplete fix for CVE-2024-33663: the HMAC key
check rejects PEM- and SSH-formatted public keys but not DER ones, so a
verifier that checks tokens with a public key and also accepts HS256 accepts
a token signed with that public key. No fixed release exists; 3.5.0 (May
2025) is the latest and upstream has been quiet since.

Not reachable here, confirmed by a security review against origin/main.
FileMorph has no asymmetric key. Every token is verified in one place
(`_decode`, app/core/tokens.py) with the symmetric JWT_SECRET and
algorithms=["HS256"]. python-jose takes the key only from that argument,
never from a kid/jwk/jku header, and there is no JWKS, OIDC or SSO code.

Per docs/patch-policy.md (no upgrade available), the ID goes on the
--ignore-vuln list with the reasoning next to the step, a review date
(2026-11-05, and at each release) and the exit conditions: migrate off
python-jose before any SSO work (that also ends PYSEC-2026-1325), or an
upstream fix. docs/security-overview.md lists it as the third accepted
advisory. A new guard, test_cve_2026_85394_ignore_still_holds, fails CI if
a second JWT verify path appears while the ignore is still in ci.yml.
Mutation-checked: an extra jwt.decode under app/ turns it red.

The changelog entry is a changelog.d/ fragment; the fragment tooling
lands with #196.

Verified: full suite 1652 passed / 79 skipped; local pip-audit on
requirements.lock with CI's flags: no known vulnerabilities, 4 ignored;
ruff clean; gitleaks and the pre-commit scope guard clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MrChengLen
MrChengLen merged commit 93c6ff1 into main Oct 6, 2026
7 checks passed
@MrChengLen
MrChengLen deleted the pr-changelog-fragments branch October 6, 2026 14:10
MrChengLen added a commit that referenced this pull request Oct 6, 2026
…e merge base

Since #196 each entry is its own file in changelog.d/, and a guard fails
when an entry is inserted at the top of CHANGELOG.md. CHANGELOG.md goes
back to the merge base's bytes, so this PR no longer touches it, and the
entry moves unchanged to changelog.d/2026-10-06-pillow-heif-docs.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant