From 42258b9a96098353641fad9305c03a4917cbb3fe Mon Sep 17 00:00:00 2001 From: m0rph3us1987 Date: Tue, 4 Aug 2026 19:35:50 +0000 Subject: [PATCH] AI Implementation feature(1090): Mastodon Post Composition and Publishing 1.00 (#7) --- docs/architecture/anniversary-matching.md | 9 ++++-- src/tenbackward/matching.py | 10 +++++-- tests/test_matching.py | 36 ++++++++++++++++++++++- 3 files changed, 49 insertions(+), 6 deletions(-) diff --git a/docs/architecture/anniversary-matching.md b/docs/architecture/anniversary-matching.md index 19c93a7..bd05d09 100644 --- a/docs/architecture/anniversary-matching.md +++ b/docs/architecture/anniversary-matching.md @@ -28,8 +28,13 @@ IANA timezone, defaulting to `Europe/Berlin` in the matcher. # Leap-Day Behavior A February 29 post matches February 29 when the current year is a leap year. -In a non-leap year it matches March 1, preserving one anniversary event for -leap-day posts. +In a non-leap year it matches March 1, but only when the post's year is +exactly ten years before the current year (`today.year − 10`). Because +`today.year − 10` is never itself a leap year whenever today is a leap year +(`10 mod 4 == 2`), the Feb 29 ↔ Feb 29 branch is reached independently of +the year-equality check: any Feb 29 post from a prior leap year is a +candidate, and its canonical URL keeps the original `YYYY/02/29//` +path. # Output and Integration diff --git a/src/tenbackward/matching.py b/src/tenbackward/matching.py index 837870e..3017098 100644 --- a/src/tenbackward/matching.py +++ b/src/tenbackward/matching.py @@ -90,12 +90,16 @@ def _is_unpublished(metadata: dict) -> bool: def _matches_anniversary(post_date: date, target_year: int, today: date) -> bool: - if post_date.year != target_year: - return False if post_date.month == 2 and post_date.day == 29: if _is_leap_year(today.year): return today.month == 2 and today.day == 29 - return today.month == 3 and today.day == 1 + return ( + post_date.year == target_year + and today.month == 3 + and today.day == 1 + ) + if post_date.year != target_year: + return False return post_date.month == today.month and post_date.day == today.day diff --git a/tests/test_matching.py b/tests/test_matching.py index 762626b..84c9538 100644 --- a/tests/test_matching.py +++ b/tests/test_matching.py @@ -133,6 +133,13 @@ def test_iter_anniversary_paths_yields_only_path(tmp_path: Path) -> None: # (2016) does not equal target_year (2015), so it does NOT match under # the strict year-equality rule. (date(2025, 3, 1), "2016-02-29", False), + # Feb 29 source (2016) in a leap current year (2028), today is Feb 29: + # target_year (2018) is not a leap year, but the Feb 29 -> Feb 29 + # leap-year branch accepts any prior Feb 29 post. + (date(2028, 2, 29), "2016-02-29", True), + # Feb 29 source (2020) in a leap current year (2028), today is Feb 29: + # same branch, also matches. + (date(2028, 2, 29), "2020-02-29", True), ], ) def test_leap_day_matching( @@ -140,7 +147,8 @@ def test_leap_day_matching( ) -> None: root = tmp_path / "_posts" / "blog" target_year = today.year - 10 - rel = f"{target_year}/{source_iso}-leap.md" + post_year = int(source_iso.split("-", 1)[0]) + rel = f"{post_year}/{source_iso}-leap.md" _write_post( root, rel, @@ -155,6 +163,32 @@ def test_leap_day_matching( assert rel not in matched_paths +def test_leap_day_feb29_matches_with_canonical_url(tmp_path: Path) -> None: + root = tmp_path / "_posts" / "blog" + rel = "2016/2016-02-29-ten-years-before-2026.md" + _write_post( + root, + rel, + body=( + "---\n" + "title: Would Be Ten Years Ago Today In 2026 Mar 1\n" + "date: 2016-02-29 08:00:00\n" + "---\n" + "body\n" + ), + ) + + today = date(2028, 2, 29) + matches = find_anniversary_matches(root, SITE_URL, today=today) + + assert len(matches) == 1 + match = matches[0] + assert match.path == rel + assert match.date == date(2016, 2, 29) + assert match.title == "Would Be Ten Years Ago Today In 2026 Mar 1" + assert match.url == "https://chaospott.de/2016/02/29/would-be-ten-years-ago-today-in-2026-mar-1/" + + def test_match_uses_datetime_frontmatter_date(tmp_path: Path) -> None: root = tmp_path / "_posts" / "blog" _write_post(