From d9f90010cbb90a9ba46af19861e0924414e14efb Mon Sep 17 00:00:00 2001 From: Derek Gulbranson Date: Sun, 11 Oct 2026 15:38:43 -0700 Subject: [PATCH] chore(release): open the 2.5 cycle Release checklist steps 8 and 9 after 2.4.0 shipped: DEFAULT_BASELINE moves to 2.4.0 and expected_since_2.4.0.toml opens empty, enrolled in all six filename rosters (four in test_ledger_guards.py, two in compare.py); the 2.3.0 rows' "open cycle" comments now say "2.4 cycle". VERSION is (2, 5, 0) with PRE_RELEASE "dev", and the release log has a 2.5.0 Unreleased entry. CITATION.cff stays at 2.4.0 by design. The default-baseline run reads 0 intentional, 0 unexplained against the published 2.4.0 wheel, as a fresh cycle should. Co-Authored-By: Claude Opus 5.5 --- docs/release_log.rst | 4 +++ nameparser/_version.py | 10 +++--- tests/v2/test_ledger_guards.py | 16 +++++---- tools/differential/compare.py | 13 +++++--- tools/differential/expected_since_2.4.0.toml | 34 ++++++++++++++++++++ 5 files changed, 61 insertions(+), 16 deletions(-) create mode 100644 tools/differential/expected_since_2.4.0.toml diff --git a/docs/release_log.rst b/docs/release_log.rst index b0da0dea..3e9a7045 100644 --- a/docs/release_log.rst +++ b/docs/release_log.rst @@ -1,5 +1,9 @@ Release Log =========== +* 2.5.0 - Unreleased + + nameparser 2.5 is under development. + * 2.4.0 - October 11, 2026 nameparser 2.4 reads post-nominal credentials much more reliably, recognizes more naming conventions, and writes credentials the usual way when it repairs case. Highlights: diff --git a/nameparser/_version.py b/nameparser/_version.py index f84d897b..1f9ef660 100644 --- a/nameparser/_version.py +++ b/nameparser/_version.py @@ -4,16 +4,16 @@ #: #: Bumped when a cycle OPENS, not when it ships, so the tree says what #: it is building. Note the cost of the tuple being purely numeric: it -#: cannot carry the dev marker, so `VERSION >= (2, 4, 0)` is already -#: true here while 2.4.0 is unreleased. Compare `__version__` instead +#: cannot carry the dev marker, so `VERSION >= (2, 5, 0)` is already +#: true here while 2.5.0 is unreleased. Compare `__version__` instead #: where that distinction matters. -VERSION = (2, 4, 0) +VERSION = (2, 5, 0) #: PEP 440 pre-release/dev segment appended to the numeric version, or #: "" for a final release. Joined WITHOUT a dot ("2.0.0rc1", not #: "2.0.0.rc1"); setuptools reads __version__ as the package version. #: #: "dev" through the cycle, cleared at release. It normalizes to -#: 2.4.0.dev0, which sorts above 2.3.0 and BELOW 2.4.0, so an install +#: 2.5.0.dev0, which sorts above 2.4.0 and BELOW 2.5.0, so an install #: from master can never masquerade as the release it precedes. -PRE_RELEASE = "" +PRE_RELEASE = "dev" __version__ = ".".join(map(str, VERSION)) + PRE_RELEASE diff --git a/tests/v2/test_ledger_guards.py b/tests/v2/test_ledger_guards.py index 0af75de4..711c4d46 100644 --- a/tests/v2/test_ledger_guards.py +++ b/tests/v2/test_ledger_guards.py @@ -358,9 +358,10 @@ def test_script_ranges_membership_is_decided() -> None: }), "expected_since_2.1.0.toml": frozenset(), # 2.2 cycle: no span-bearing rule "expected_since_2.2.0.toml": frozenset(), # 2.3 cycle: no span-bearing rule - # The open cycle's rules (#383/#479, 2026-09-13) are literal + # The 2.4 cycle's rules (#383/#479, 2026-09-13) are literal # names or alternations of names, and copy no script range. "expected_since_2.3.0.toml": frozenset(), + "expected_since_2.4.0.toml": frozenset(), # open cycle, no rules yet } #: The leading `fix(...)`/`feat(...)` tag of a rule's `issue`, which is @@ -6987,6 +6988,7 @@ def _claim(rule: dict) -> _Claim: "fix(#614) a trailing title is read before the particle chain": _Claim(1, ('family', 'title'), "4536c33ed2a2", None), }, + "expected_since_2.4.0.toml": {}, # open cycle, no rules yet } @@ -7088,11 +7090,12 @@ def test_every_rule_claims_the_recorded_share_of_the_corpus() -> None: #: of each fact, since two means one of them goes quietly stale. _CROSS_RULE_WINNERS: dict[str, dict[str, str]] = { "expected_since_2.2.0.toml": {}, - # The open cycle carries #383/#479's three rules since - # 2026-09-13 and still no contest: literal names or alternations - # of names, reaching no corpus name in common, so no diff is - # claimed twice. + # The 2.4 cycle carried #383/#479's three rules from 2026-09-13 + # and no contest: literal names or alternations of names, reaching + # no corpus name in common, so no diff is claimed twice. "expected_since_2.3.0.toml": {}, + # open cycle: no rules, so no contest + "expected_since_2.4.0.toml": {}, "expected_since_1.4.0.toml": { # Spelled out since #508: the bare `fix(comma-family)` this row # carried is a prefix of THREE rules in this ledger (measured @@ -8799,10 +8802,11 @@ def test_a_rule_reaching_no_corpus_name_says_why_it_is_kept() -> None: ], "expected_since_2.1.0.toml": [], "expected_since_2.2.0.toml": [], - # The open cycle's three rules (#383/#479, 2026-09-13) nest with + # The 2.4 cycle's three rules (#383/#479, 2026-09-13) nest with # nothing: no two of them share a corpus name, so no pair is a # contest at all, let alone a wide-first one. "expected_since_2.3.0.toml": [], + "expected_since_2.4.0.toml": [], # open cycle, no rules yet } diff --git a/tools/differential/compare.py b/tools/differential/compare.py index fad7c8b6..2fe88254 100644 --- a/tools/differential/compare.py +++ b/tools/differential/compare.py @@ -31,7 +31,7 @@ FIELDS = ("title", "first", "middle", "last", "suffix", "nickname", "maiden") -DEFAULT_BASELINE = "2.3.0" +DEFAULT_BASELINE = "2.4.0" REPO_ROOT = HERE.parents[1] #: The v2 API's names for the same seven roles FIELDS names in v1 #: vocabulary. Both are compared from baseline 2.0 on. @@ -1748,11 +1748,13 @@ class _ShapeMismatch(NamedTuple): #: diffs from nothing. _RECORDED_DIFFS: dict[str, dict[str, tuple[str, ...]]] = { "expected_since_2.2.0.toml": {}, - # The open cycle carries #383/#479's three rules since - # 2026-09-13, and no contest among them: each is a literal name - # or alternation of names, and no two reach the same corpus - # name, so the run finds no contested diff to adjudicate. + # The 2.4 cycle carried #383/#479's three rules from 2026-09-13, + # and no contest among them: each is a literal name or + # alternation of names, and no two reach the same corpus name, + # so the run finds no contested diff to adjudicate. "expected_since_2.3.0.toml": {}, + # open cycle: no rules, so nothing for a second one to contest + "expected_since_2.4.0.toml": {}, "expected_since_1.4.0.toml": { "Andrews, M.D.": ("given", "suffix"), # #613's PR review (2026-10-06): a tolerated CJK case row, H1's @@ -2324,6 +2326,7 @@ class _ShapeMismatch(NamedTuple): # cases.py row, a render pin or a v1 bank -- so not one of them # meets the population's literal clause and none takes a row here. "expected_since_2.3.0.toml": {}, + "expected_since_2.4.0.toml": {}, # open cycle, no watched name yet } diff --git a/tools/differential/expected_since_2.4.0.toml b/tools/differential/expected_since_2.4.0.toml new file mode 100644 index 00000000..dd986e12 --- /dev/null +++ b/tools/differential/expected_since_2.4.0.toml @@ -0,0 +1,34 @@ +# Ledger for baseline 2.4.0 -- what changes for a user upgrading from +# the previous minor. Same rule grammar as the other ledgers: `issue`, +# `name_regex` and `fields` are every one of them required. Neither +# narrowing key may stand on its own -- #451 banned a rule with +# `fields` and no regex, #456 banned the reverse -- so a rule here +# narrows by name AND by role, never by just one. +# +# Opened empty the day 2.4.0 shipped (AGENTS.md release step 8), and it +# is opened before it has anything to say because DEFAULT_BASELINE and +# the ledger are coupled: _allowlist_for treats a missing file as a +# hard error, on the reasoning that an absent ledger classifies nothing +# and would make every diff report as unexplained. So the baseline +# could not advance to 2.4.0 without this file. +# +# Do not copy rules across from expected_since_2.3.0.toml: that ledger +# classifies what 2.4 changed on top of 2.3, and a rule copied without +# checking either over-matches -- hiding a real 2.5 regression behind a +# 2.4-era label -- or never fires at all. Where a 2.5 change is visible +# from several baselines the rule may legitimately appear in more than +# one file, but each copy is checked against its own run rather than +# assumed. +# +# tests/v2/test_differential.py permits exactly one empty ledger, the +# one DEFAULT_BASELINE names -- which is this file. +# +# tests/v2/test_ledger_guards.py and tools/differential/compare.py +# enroll this file, empty, in every roster keyed by ledger filename +# (AGENTS.md release step 8 lists them). The first rule that copies a +# script range, a CJK honorific or a Latin vocabulary has to be +# recorded in its roster or the sweep fails. +# +# There is deliberately no `change = []` line. TOML forbids appending a +# [[change]] table to a statically defined array, so that line would +# block the first entry.