From 51c7dfdbd73948ea48091782533aea05ab4ddd3e Mon Sep 17 00:00:00 2001 From: Rudy Mizrahi Celekli <47457359+rudycelekli@users.noreply.github.com> Date: Mon, 19 Oct 2026 13:49:24 -0400 Subject: [PATCH] fix(rank): keep every --exclude key out of the sweep when the list has spaces (#641) * fix(rank): keep every --exclude key out of the sweep when the list has spaces `sweep --exclude "a_b, c_d"` split on bare commas, so " c_d" matched no entry and every key after the first was swept. On an --all run that expired a re-scored entry whose fresh deadline apply then re-ranked, so the report listed it as both expired and shortlisted. Strip each key. Co-Authored-By: Claude Opus 5.5 * changelog: place the #641 entry under [Unreleased] Co-Authored-By: Claude Opus 5.5 --------- Co-authored-by: Claude Opus 5.5 Co-authored-by: Mads Lorentzen --- CHANGELOG.md | 7 +++++++ tests/test_rank_state.py | 13 +++++++++++++ tools/rank_state.py | 4 +++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 102dac0a..12c2192d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -89,6 +89,13 @@ per-file diff commands. "Ørsted" raised a `UnicodeDecodeError` traceback and stopped `/rank` at Step 1. It now exits 1 with a one-line message naming the file and asking for a CSV UTF-8 save, as `load_state` does for invalid JSON. +- **`rank_state.py sweep --exclude` ignores spaces after the commas** (#641, `tools/rank_state.py`, + `tests/test_rank_state.py`) - rank.md asks for the keys scored this run as a comma-separated list, and + `"a_b, c_d"` kept every key after the first in the sweep, because `" c_d"` matched no entry. On an + `--all` run, a re-scored entry whose stored deadline had passed was written `expired` and reported under + "newly expired" by the sweep, then re-ranked by `apply` from its fresh deadline, so the same job appeared + as both expired and shortlisted. Keys are now stripped before matching. + ## [1.7.3] - 2026-10-19 ### Added diff --git a/tests/test_rank_state.py b/tests/test_rank_state.py index a7233da3..08b296d8 100644 --- a/tests/test_rank_state.py +++ b/tests/test_rank_state.py @@ -259,6 +259,19 @@ class Sweep(RankStateCase): self.assertEqual(out["swept"], 1) self.assertEqual(self.read_state()["new_past"]["status"], "new") + def test_excluded_keys_tolerate_spaces_after_commas(self): + self.write_state( + { + "a": entry(status="ranked", deadline="2026-09-01"), + "b": entry(status="ranked", deadline="2026-09-01"), + "other": entry(status="ranked", deadline="2026-09-01"), + } + ) + out = self.run_tool("sweep", "--write", "--exclude", "a, b") + self.assertEqual([r["key"] for r in out["newly_expired"]], ["other"]) + self.assertEqual(out["swept"], 1) + self.assertEqual(self.read_state()["b"]["status"], "ranked", "a key scored this run is never swept") + def test_without_write_nothing_is_persisted(self): self.write_state({"past": entry(status="ranked", deadline="2026-09-01")}) out = self.run_tool("sweep") diff --git a/tools/rank_state.py b/tools/rank_state.py index 93e5b898..1f03c3d5 100644 --- a/tools/rank_state.py +++ b/tools/rank_state.py @@ -230,7 +230,9 @@ def cmd_candidates(args) -> int: def cmd_sweep(args) -> int: doc, seen = load_state(args.state) today = args.today - exclude = {k for k in (args.exclude or "").split(",") if k} + # "a_b, c_d" is as natural a comma-separated list as "a_b,c_d"; keys never + # carry surrounding whitespace, so a space must not un-exclude a key. + exclude = {k.strip() for k in (args.exclude or "").split(",") if k.strip()} expired, closing, unparseable, checked = [], [], [], 0 for key, entry in seen.items():