Skip to content

Fail pull requests that add changelog fragments towncrier drops - #2324

Draft
MaxGhenis wants to merge 8 commits into
mainfrom
changelog-fragment-check
Draft

MaxGhenis wants to merge 8 commits into
mainfrom
changelog-fragment-check

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 11, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #2314. Also fixes the fragment example from #2162 (item 3).

The defect

towncrier writes a changelog.d fragment to CHANGELOG.md only when its name carries a type configured in pyproject.toml (breaking, added, changed, fixed, removed). It never reads, and never deletes, any other file there, and it leaves an empty fragment out. None of this produces a warning. Checked on main at 6d89338:

Change

A Changelog fragments check (.github/workflows/pr_changelog.yaml runs .github/check_changelog_fragments.py). It has no path filter and runs for merge queue groups, so it can be made required. It fails a PR that adds or changes a file in changelog.d/ that:

  • is not named <slug>.<type>.md, with a type read from pyproject.toml at the PR's head (a PR can add a type and use it). The message says "no towncrier type" only when towncrier reads none. Other forms towncrier does consume, such as 2314.fixed.1.md (which towncrier create writes) or x.fixed, are reported as not in this repository's form.
  • is empty or blank, which towncrier leaves out;
  • is not UTF-8, which towncrier would write with replacement characters;
  • is a symbolic link or a submodule entry, or has a file name that is not UTF-8. towncrier follows a link when it builds, so a missing target stops the release.
  • sits in a subdirectory, which towncrier never reads;
  • shares towncrier's (issue, type, counter) key with another fragment, in the PR or already on main, such as 7.fixed.md and 007.fixed.md. That stops towncrier build with "multiple files", and with it the release.

It also fails a PR that changes pyproject.toml or changelog.d/ when its merge would leave no typed fragment. Those are the paths that start the release workflow, and bump_version.py would then exit 1 with nothing to release.

It only warns when:

  • the PR adds no fragment;
  • a fragment starts with a list marker. towncrier adds the bullet itself, so this renders as - - ; CHANGELOG.md has 131 such lines.
  • the directory already holds an untyped file the PR doesn't touch.

Deleting a fragment always passes, and so do the names towncrier ignores (.gitkeep, README.md). Each problem is a GitHub annotation on the file.

towncrier check --compare-with origin/main does reject untyped names. I didn't use it because it scans the whole directory rather than the PR's files, requires every PR to add a fragment, lets empty fragments through, and accepts x.fixed and x.fixed.txt.

.github/bump_version.py now counts only the fragments towncrier consumes:

  • It reads the fragment directory, the types, the ignore list and the orphan prefix from [tool.towncrier] the way towncrier does, and parses names with an exact port of parse_newfragment_basename.
  • It prints a ::warning for each untyped file. With no typed fragment it exits 1, which was already its behaviour for an empty directory.
  • It refuses a configuration it would read differently from towncrier: a towncrier.toml file, sections, or an issue_pattern.
  • When ignore is configured, towncrier build stops at an untyped file, so the script stops there too, before changing the version.
  • It stays self-contained (standard library only), and the check imports its parser.

Deleted the 93 untyped files (table below). Their code is released; converting them to typed fragments now would file 2.86–2.99 notes under the next version. Every file was checked:

  • its PR is MERGED, and the PR's merge commit is the first-parent commit that added the file;
  • the first version commit after it is on PyPI (2.86.1 to 2.99.1, 70 releases);
  • 69 of those 70 releases are git-tagged. 2.88.23 (4 files) is on PyPI but has no tag; I did not establish why.

The audit script and its JSON output are in ~/reviews/uk-changelog-fragment-check-2026-10-11/.

Docs:

Effect on main

  • A push that starts versioning with no typed fragment fails the Versioning job. bump_version.py exits 1 with "No changelog fragments with a towncrier type". Before, it cut an empty No significant changes. release, because the untyped files kept the directory from ever being empty. The new check fails such a PR before it merges. The check is not a required one, so a PR merged past it can still do this. Since July every merge that triggered versioning added a fragment.
  • This PR's own merge releases a patch from 2314.fixed.md.
  • Open PRs:

Invariants (tested)

  1. Config: bump_version reads the same types (in order), directory, orphan prefix and strictness as towncrier's own loader. This holds for this repository and seven other configurations:

    • default types;
    • default directory;
    • the type array;
    • fragment tables with ignore;
    • a template file with a custom orphan prefix;
    • a package taken from the project name;
    • a type called md.

    It refuses a towncrier.toml, sections and issue_pattern.

  2. Names: for any file name, parse_fragment_name equals towncrier's parse_newfragment_basename (Hypothesis, 500 examples, four type sets).

  3. Directories (Hypothesis, 60 directories × 8 configurations):

    • the files bump_version counts are exactly the ones towncrier's find_fragments consumes, with the same types;
    • it calls a file untyped exactly when towncrier's strict mode rejects the directory;
    • infer_bump stops exactly when towncrier build would stop or has nothing to write.
  4. Check ⊆ towncrier:

    • every name the check accepts, towncrier consumes with the type the name shows and no counter, also when a type is called md;
    • no name towncrier drops is accepted;
    • the check says "no towncrier type" exactly when towncrier reads none;
    • every <slug>.<type>.md passes.
  5. Rendering, with the real towncrier build --draft:

    • each fragment the check accepts appears exactly once with one bullet;
    • the fragment it warns about renders as - - ;
    • the untyped, empty and blank fragments it rejects are missing from the output;
    • the non-UTF-8 fragment it rejects is written with a replacement character.
  6. PR behaviour, on real git repositories:

    • fails exactly the added, changed or renamed-to files that towncrier would mishandle or that would stop the build. That includes a clash with a fragment already on main, a symbolic link (dangling, resolving, or replacing a file), a submodule entry, a file name that is not UTF-8, and an ignored name in a subdirectory.
    • passes deletions, ignored names and this PR's shape (deleting untyped files and adding a typed fragment);
    • only warns about untouched files and a PR with no fragment;
    • gives a PR whose only fragment fails that one error, without also warning that it has no fragment;
    • honours a type added in the same PR;
    • fails a PR that starts the release workflow with no typed fragment left, and only warns when one is waiting or when the PR starts no release. The trigger paths it uses equal on.push.paths in versioning.yaml.
    • exits 2 when pyproject.toml has no [tool.towncrier], or a towncrier.toml exists.
  7. Bump:

    • the bump depends only on typed fragments: breaking is major, added or removed minor, any other type patch;
    • untyped files never change it;
    • with no typed fragment the script exits 1 and leaves the version alone (property test plus end-to-end runs of the script).

All 77 tests in policyengine_uk/tests/code_health/test_changelog_fragments.py pass locally against the locked towncrier 25.8.0, hypothesis 6.168.2 and pytest 8.4.1. They also pass against towncrier 26.9.0, which the unpinned release job installs today. Each of 29 hand-made mutants of the two scripts fails at least one test, including the five the review found surviving. The repo's cached-array scanner reports nothing on the test file. actionlint and ruff are clean.

Review

GPT-6.1 Sol reviewed head e946276 (~/reviews/uk-changelog-fragment-check-2026-10-11/review-2324-sol.md): request changes. It confirmed that the parser matches towncrier, all 93 deletions, the figures above, and the #2306 and #2129 notes. Commit d95d020 addresses its findings:

Finding What changed
1. The test file tripped the cached-array scanner (.partition on a call result), failing Test The test parses annotations with a regex; the scanner reports nothing on the file.
2. "Has no towncrier type" was said of names towncrier consumes (2314.fixed.1.md) The message now says that only when towncrier reads no type.
3. Non-UTF-8 fragments were said to be unreadable The message and this description say what towncrier does: it writes replacement characters. A rendering test pins it.
4. Symbolic links and submodule entries passed The check reads git modes and rejects both.
5. Five mutants survived New tests kill each.
6. A type called md broke the invariant; some config was silently ignored Fixed and tested; unsupported config is refused; the project-name fallback and strict mode are mirrored.
7. Nits: a confusing extra warning, unescaped annotations in bump_version.py, a raw error for a non-UTF-8 path, a vacuous assertion Each fixed and tested.
8. A PR changing only pyproject.toml could pass and turn Versioning red The check now fails it.

Not changed: the release job installs unpinned towncrier (versioning.yaml), which #2306 replaces with the version in uv.lock. A delta review of d95d020 is pending.

Coordination

#2306 (W2). Either order now works. #2306's head 0879f6c counts fragments with the tip's own bump_version.py when it has the new helpers, and the old way otherwise. On a local merge of that head with this PR's head, all 97 tests in test_prepare_release.py and test_changelog_fragments.py pass. Its allowlist already accepts this module's wheel-mode skip (code_health/*, reason \.github).

#2129 (keep uv.lock's own version in step, draft) also edits bump_version.py. Merging the two conflicts on one import line. One of #2129's tests then needs a [tool.towncrier] table in its fixture pyproject.toml; the fix is in this comment. With it, all 86 tests in both files pass on the merge.

Not done here

  • Making Changelog fragments a required check is a repository setting for an admin. Main has no required checks today, so the check is advisory.
  • The 131 existing - - lines in CHANGELOG.md. Editing CHANGELOG.md by hand is against the repo's rules.

axiom: n/a: CI and changelog tooling

The 93 deleted files: PR, merge date, first release, note
File PR Merged Released in Note
1031.md #1595 2026-04-15 2.86.1 Removed the inferred is_parent formula so datasets provide parent status directly.
1032.md #1604 2026-04-15 2.86.10 - Added Universal Credit carer element to extended childcare entitlement work-requirement exempti…
1042.md #1596 2026-04-15 2.86.2 Clarified that Tax-Free Childcare age parameters are exclusive upper thresholds.
1043.md #1602 2026-04-15 2.86.8 - Improved Tax-Free Childcare expected income and self-employment start-up period handling.
1044-explicit-leave.md #1601 2026-04-15 2.86.7 - Added explicit Tax-Free Childcare adoption and shared parental leave work-condition inputs.
1044.md #1600 2026-04-15 2.86.6 - Added Tax-Free Childcare temporary absence from work handling.
1046.md #1597 2026-04-15 2.86.3 Added the Tax-Free Childcare UK connection eligibility condition.
1047.md #1603 2026-04-15 2.86.9 - Added Tax-Free Childcare eligible declaration period handling.
1048.md #1599 2026-04-15 2.86.5 - Added Tax-Free Childcare looked-after-child eligibility handling.
1055.md #1598 2026-04-15 2.86.4 - Refactored universal childcare entitlement age parameters into an age folder.
1056.md #1606 2026-04-15 2.86.12 - Updated Tax-Free Childcare Universal Credit disqualification to include awards reduced to nil.
1065.md #1605 2026-04-15 2.86.11 - Updated targeted childcare entitlement tax credit criteria to match historical and current regu…
1015.md #1612 2026-04-16 2.86.13 - Restructure minimum wage parameter into apprentice and non-apprentice (age-based scale) files
634.md #1610 2026-04-16 2.86.13 - Fix incorrect description on non-residential SDLT rate parameter (was labelled "residential")
651.md #1614 2026-04-16 2.86.13 - Clarify variable descriptions for self-employment income and corporate wealth
665.md #1608 2026-04-16 2.86.13 - Remove redundant name metadata tag from parameter YAML files
930.md #1613 2026-04-16 2.86.13 - Add test suite verifying that key parametric reforms produce non-zero impacts
950.md #1615 2026-04-16 2.86.13 - Add Scottish Carer Support Payment, replacing Carer's Allowance for Scottish residents
1005.md #1622 2026-04-17 2.88.2 - Add is_about_to_start_paid_work and is_newly_self_employed as Person-level boolean input va…
1616.md #1620 2026-04-17 2.88.2 - Add Ofgem Q2 2026 (April–June 2026) energy price cap of £1,641.
395.md #1624 2026-04-17 2.88.2 - Parametrise the Marriage Allowance transferor-eligible tax bands as `gov.hmrc.income_tax.allowa…
420.md #1623 2026-04-17 2.88.2 - Pay the two-carer Carer Premium rate when a benefit unit has three or more carers. The previous…
201.md #1638 2026-04-20 2.88.9 - Add regression tests pinning the existing UC vs Housing Benefit mutual-exclusion contract in `h…
257.md #1637 2026-04-20 2.88.9 - Fix AttributeError in cps.marriage_tax_reforms when reform was instantiated with `child_edu…
396.md #1639 2026-04-20 2.88.9 - Fill in 2020 and 2021 carbon intensity values for all 12 consumption categories by linear inter…
451.md #1609 2026-04-20 2.88.9 - Change hours_worked variable label to "Annual hours worked" for clarity.
623.md #1636 2026-04-20 2.88.7 - Set Marriage Allowance take-up rate parameter to 0.5 (HMRC outturn ~2.1m claimants of ~4.2m eli…
953.md #1635 2026-04-20 2.88.9 - Align state pension triple lock with published outturn rates so basic and new state pension amo…
legal-child-qyp-rules.md #1655 2026-04-28 2.88.10 - Removed generic child or qualifying young person predicates and replaced them with lowercase pr…
1008.md #1643 2026-05-01 2.88.11 - Add scottish_funded_early_learning_childcare_eligible and the supporting parameters under `go…
257-winter-fuel.md #1650 2026-05-01 2.88.11 - Use np.isin instead of OR-chained equality in winter_fuel_allowance.
289.md #1651 2026-05-01 2.88.11 - Consolidate UC work allowance parameters (with_housing / without_housing) into a single `wo…
326.md #1649 2026-05-01 2.88.11 - Cite SI 2016/909 reg. 2 on the benefit cap parameter, which is where the 2016-11-07 values orig…
45.md #1641 2026-05-01 2.88.11 - Add marginal_tax_rate_wrt_employer_cost, a person-level variable that reports the share of a…
580.md #1648 2026-05-01 2.88.11 - Replace the carbon intensity chart in the carbon tax doc with a horizontal bar ranking categori…
649.md #1647 2026-05-01 2.88.11 - Add README and index.yaml metadata for the DCMS parameters and variables folders.
737.md #1642 2026-05-01 2.88.11 - Add unit tests for the two CPS marriage tax reforms (expanded_ma_reform and `marriage_neutral…
853.md #1607 2026-05-01 2.88.11 - Rename labor to labour in LSR parameter and variable paths, labels, and descriptions to use…
414.md #1640 2026-05-02 2.88.12 - Remove trading_allowance, property_allowance, and dividend_allowance variables that were…
1666.md #1667 2026-05-08 2.88.14 - Prevent children in a married benefit unit from being assigned (negative) Marriage Allowance an…
fuel-duty-litre-rate.md #1680 2026-05-20 2.88.20 - Compute fuel duty directly as calibrated petrol and diesel litres multiplied by the statutory d…
fuel-volume-uprating.md #1679 2026-05-20 2.88.19 - Uprate petrol and diesel spending with HMRC/OBR road-fuel volume growth rather than CPI when pr…
1681.md #1690 2026-05-23 2.88.21 - Fixed PolicyBench audit issues in Pension Credit, Capital Gains Tax, and income-tax source orde…
1688.md #1691 2026-05-23 2.88.22 - Updated published 2026-27 benefit and tax parameters, including Child Benefit, Pension Credit,…
307.md #1663 2026-05-23 2.88.23 (no tag) - Add YAML tests for household_equivalisation_bhc and household_equivalisation_ahc covering t…
340.md #1664 2026-05-23 2.88.23 (no tag) - Add a structural test that every parameter description ends with a period, and bring the existi…
676.md #1672 2026-05-23 2.88.23 (no tag) Add the Rural Fuel Duty Relief Scheme.
866.md #1662 2026-05-23 2.88.23 (no tag) Add a disability basic income reform option.
1171.md #1706 2026-06-01 2.88.33 - Add a docs page comparing PolicyEngine UK's nowcasting approach to the Resolution Foundation's…
1437-revert.md #1745 2026-06-01 2.88.39 - Revert the fiscal-year uprating planning docs page (#1716) per maintainer request.
1644-childcare-registry.md #1703 2026-06-01 2.88.26 - Split the childcare_entitlements umbrella entry in programs.yaml into three rows (universal…
1654-revert.md #1744 2026-06-01 2.88.38 - Revert the Benefits-in-Kind planning docs page (#1707) per maintainer request.
1670.md #1693 2026-06-01 2.88.25 - Exclude high_value_council_tax_surcharge from household_tax and gov_tax when `gov.contrib…
1671.md #1694 2026-06-01 2.88.27 - Document council_tax_less_benefit as a public-facing convenience variable used by downstream…
373.md #1734 2026-06-01 2.88.28 - Document the actual policy start years for Attendance Allowance (1971), Carer's Allowance (1976…
587-format.md #1743 2026-06-01 2.88.32 - Apply ruff formatting to the validation notebook (blank line after the warnings import) so the…
587.md #1696 2026-06-01 2.88.29 - Suppress Python warnings at the top of the validation notebook so re-executions don't surface p…
623-takeup-start-date.md #1698 2026-06-01 2.88.31 - Date the Marriage Allowance take-up rate parameter from 2015-04-06 (when the allowance came int…
747.md #1697 2026-06-01 2.88.30 - Flesh out the fuel duty documentation page with the household-level computation formula, a sect…
771-cost-of-living-docs.md #1708 2026-06-01 2.88.35 - Add a documentation page covering the Cost-of-Living Payments (2022-23 and 2023-24) and the 202…
771-tax-credits-docs.md #1710 2026-06-01 2.88.36 - Add a documentation page covering Working Tax Credit and Child Tax Credit — both elements/hours…
920.md #1695 2026-06-01 2.88.24 - Drop dev dependencies that are already brought in transitively by policyengine-core as runtim…
local-dataset-filepaths.md #1747 2026-06-02 2.88.40 - Allow UK simulations to load local dataset file paths, including materialized H5 files passed b…
1755.md #1756 2026-06-04 2.88.42 - Floor the Universal Credit housing costs element at zero after non-dependent deductions.
uc-childcare-work-condition.md #1757 2026-06-04 2.88.43 - Fixed Universal Credit childcare work-condition eligibility so couples require all adults to be…
1379.md #1701 2026-06-05 2.88.48 - Document the methodology and sources for the 2031+ long-run values in yoy_growth.yaml (CPI pi…
1400.md #1711 2026-06-05 2.88.49 - Add a standalone documentation page summarising the Resolution Foundation's nowcasting methodol…
1458-pawhp-refs.md #1702 2026-06-05 2.88.45 - Add legislative references and tidy labels for the Pension Age Winter Heating Payment (Scotland…
1632.md #1717 2026-06-05 2.88.51 - Add a State Pension documentation page at docs/book/programs/gov/dwp/state-pension.md describ…
368.md #1733 2026-06-05 2.88.44 - Clarify the current_education label as "Current education enrolment" and add a docstring dist…
609.md #1704 2026-06-05 2.88.46 - Add the 2023-24 Cost-of-Living Payments to the cost_of_living_support_payment parameters — £9…
721.md #1715 2026-06-05 2.88.50 - Document the relationship between PolicyEngine UK's annual-NI computation and HMRC's "annual cu…
771-housing-benefit.md #1705 2026-06-05 2.88.52 - Add a Housing Benefit documentation page covering eligibility (legacy-only working-age claimant…
778.md #1713 2026-06-05 2.88.47 - Add three worked benefit scenarios at `policyengine_uk/tests/policy/integration/entitledto_scen…
1765.md #1766 2026-06-06 2.88.55 - Correct the Class 4 National Insurance annual maximum calculation to use the current main Class…
745.md #1700 2026-06-06 2.88.58 - Expand the Universal Credit documentation page: add a "How PolicyEngine computes Universal Cred…
746.md #1699 2026-06-06 2.88.57 - Expand the National Insurance documentation page with a per-class computation section, add Clas…
771-disability-legacy-docs.md #1709 2026-06-06 2.88.59 - Add a documentation page covering Disability Living Allowance (mobility + self-care components,…
class4-maximum-steps.md #1768 2026-06-06 2.88.56 - Apply the Class 4 National Insurance annual maximum's Step Six and Step Nine calculations from…
tv-licence-fee-2026.md #1767 2026-06-06 2.88.54 - Update TV licence fee parameters for 2024-25, 2025-26, and 2026-27.
council-tax-reduction-kingston-upon-thames.md #1771 2026-06-07 2.88.62 - Add working-age Council Tax Reduction for Kingston upon Thames.
council-tax-reduction-merton.md #1770 2026-06-07 2.88.61 Add Merton working-age Council Tax Reduction.
council-tax-reduction-national.md #1769 2026-06-07 2.88.60 - Add national Council Tax Reduction support for Scotland, Wales, and English pensioner household…
council-tax-reduction-newham.md #1772 2026-06-07 2.88.63 - Add working-age Council Tax Reduction for Newham.
council-tax-reduction-oxford.md #1774 2026-06-07 2.88.65 Add Oxford working-age Council Tax Reduction.
council-tax-reduction-westminster.md #1773 2026-06-07 2.88.64 - Add working-age Council Tax Support for Westminster.
1780.md #1780 2026-06-17 2.89.1 - Add bus_fare_spending Household input variable (COICOP 7.3.2 bus & coach fares), the companio…
1785.md #1785 2026-06-18 2.89.2 - Add employment_sector (public/private) and sic_industry_division as Person-level input vari…
1799.md #1801 2026-07-23 2.89.3 - Add person_bus_fare_spending, allocating household bus and coach fare spending across members…
cgt-rate-effective-dates.md #1806 2026-08-12 2.90.1 - Corrected the effective date of the capital gains tax rate rise to 30 October 2024, as Finance…
capital-gains-realisation-response.md #1803 2026-08-13 2.90.2 - Fixed the capital gains realisation response, which measured the baseline marginal rate on a br…
1826.md #1828 2026-08-28 2.92.1 - Fix a units bug in dynamics/participation.py: impute_wages_for_nonworkers divided earnings…
uprate-electricity-and-gas-consumption.md #1860 2026-09-18 2.99.1 - Uprate electricity_consumption and gas_consumption. Both variables declare CPI uprating, bu…

🤖 Generated with Claude Code

MaxGhenis and others added 5 commits October 11, 2026 12:47
towncrier writes a fragment to CHANGELOG.md only when its name carries a
configured type; any other file in changelog.d is never read or deleted,
and an empty fragment is left out. Add a Changelog fragments check that
fails a PR adding or changing such a file, and make bump_version.py count
only the fragments towncrier consumes, warning about the rest.

Refs #2314

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each was added by a merged PR whose release is on PyPI (2.86.1 to 2.99.1),
but towncrier never consumed it, so none of their notes reached
CHANGELOG.md and every versioning run since counted them. Converting
them to typed fragments now would file old notes under the next version.
The PR lists each file with its PR, merge date, release and note.

Refs #2314

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Property and differential tests: bump_version reads the same config and
names as towncrier (parse_toml, parse_newfragment_basename, find_fragments)
across six configurations; every name the check accepts towncrier consumes
with that type; the real towncrier build renders exactly the fragments the
check accepts; the check on a real git repository fails exactly the files
towncrier would drop or that would stop the build; and the bump depends
only on typed fragments. Also catch two fragments that share towncrier's
key, which stops towncrier build and the release.

Refs #2314

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A required check must also report on merge_group, or the queue waits on
it; diff a group against its base, as pr_code_changes.yaml does in #2306.

Refs #2314

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Refs #2314

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#2129 makes bump_version.py move uv.lock's own version too and stop when
the lock is missing; the end-to-end bump tests then still pass.

Refs #2314

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis added a commit that referenced this pull request Oct 11, 2026
#2324 makes bump_version.py count only the fragments towncrier consumes
and exit when there are none. prepare_release.py now uses that script's
own classification when it has one (so a commit with only untyped files
releases nothing) and counts every file otherwise, as before. The tests
pass with main's bump_version.py and with #2324's, so the two pull
requests can land in either order. Based on the patch from #2324's
session.

Also merges #2303 (the stale #1968 xfail), which this pull request's
own check rejects as an XPASS until it lands.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis and others added 2 commits October 11, 2026 19:25
…rigger

Sol's review of #2324 (~/reviews/uk-changelog-fragment-check-2026-10-11):
- the test file tripped the cached-array scanner (.partition on a call
  result); parse annotations with a regex instead;
- say a name has no towncrier type only when towncrier reads none; other
  forms it consumes (x.fixed.1.md, x.fixed) are named as such;
- a non-UTF-8 fragment is written with replacement characters, not dropped;
- reject symbolic links, submodule entries and non-UTF-8 file names;
- a type called md no longer breaks 'accepted means consumed as shown';
- bump_version refuses towncrier.toml, sections and issue_pattern, follows
  the project-name package fallback, and stops where towncrier build is
  strict (ignore configured); its annotations are escaped;
- fail a PR that starts the release workflow with no typed fragment left;
- tests for each, and for the five mutants that survived.

Refs #2314

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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.

Untyped changelog fragments are silently left out of CHANGELOG.md

1 participant