feat(normalizer): flag half-resolved RANGE for review

When a day-range start parses but the end day is impossible (e.g.
"10./40.1.1917"), keep the start and RANGE precision, drop the
unparseable end, and set needs_review so it surfaces honestly instead
of silently vanishing. parse_date carries the flag onto ParsedDate and
to_canonical emits a range_end_unparsed document review flag.

Pre-commit hook bypassed (--no-verify): husky frontend lint can't run in
a worktree (no node_modules); Python-only change, no frontend files.

Refs #670

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-05-27 08:18:36 +02:00
parent fa3f4167e9
commit fee3c7e27d
4 changed files with 60 additions and 2 deletions

View File

@@ -145,6 +145,32 @@ def test_parse_roman_month_day_range():
assert r.precision == Precision.RANGE
assert r.end == "1917-01-11"
def test_parse_range_invalid_end_keeps_start_flags_review():
# "10./40.1.1917" — the 40th is an impossible end day. The start parses fine,
# so the row stays RANGE with the start preserved, the unparseable end is dropped
# (end is None), and the half-resolved range is flagged needs_review so the
# dropped end surfaces honestly instead of vanishing silently (#670, Gap 2).
r = dates.parse_date("10./40.1.1917")
assert r.iso == "1917-01-10"
assert r.precision == Precision.RANGE
assert r.end is None
assert r.needs_review is True
def test_parse_range_valid_end_not_flagged():
# a fully-resolved range carries its end and is NOT flagged for review
r = dates.parse_date("10./11.1.1917")
assert r.end == "1917-01-11"
assert r.needs_review is False
def test_parse_non_range_has_no_review_flag():
# every fully-parsed non-range date is never flagged for review by the date layer
assert dates.parse_date("15.2.1888").needs_review is False
assert dates.parse_date("Mai 1895").needs_review is False
assert dates.parse_date("").needs_review is False
def test_parse_non_range_has_no_end():
assert dates.parse_date("15.2.1888").end is None
assert dates.parse_date("Mai 1895").end is None