feat(release): drafter prefers curated [Unreleased] notes; executor moves them instead of duplicating

The readiness drafter transcribed raw commit subjects even when
[Unreleased] carried curated prose, and the executor inserted its entry
below a still-populated [Unreleased] — shipping the same content twice
in two qualities. The drafter now uses the curated body as the release
entry when present (transcription stays the fallback; completeness gaps
still police curation), and the executor empties [Unreleased] as it
stamps the entry. [Unreleased] itself catches up with the feedback
round, the dense tooltip passes, and the slave-CI fix.
This commit is contained in:
Renn F
2026-07-15 21:04:46 +02:00
parent 657018eb14
commit bdb0dd6cdd
5 changed files with 109 additions and 5 deletions
@@ -562,3 +562,26 @@ async def test_release_push_argv_uses_extraheader_not_url_token(
== f"http.extraheader=Authorization: Basic {expected_basic}"
)
assert "push" in push_argv[c_idx + 2 :]
def test_insert_changelog_entry_empties_unreleased_body() -> None:
existing = (
"# Changelog\n\nintro\n\n## [Unreleased]\n\n### Added\n\n"
"- **Curated bullet.**\n\n## [0.24.0] - 2026-07-14\n\n- old\n"
)
entry = "## [0.25.0] - 2026-07-15\n\n### Added\n\n- **Curated bullet.**\n"
result = re._insert_changelog_entry(existing, entry)
unreleased = result.split("## [Unreleased]")[1].split("## [0.25.0]")[0]
assert "Curated bullet" not in unreleased
assert result.count("Curated bullet") == 1
assert result.index("## [Unreleased]") < result.index("## [0.25.0]")
assert result.index("## [0.25.0]") < result.index("## [0.24.0]")
assert "- old" in result
def test_insert_changelog_entry_without_unreleased_is_unchanged_behavior() -> None:
existing = "# Changelog\n\n## [0.24.0] - 2026-07-14\n\n- old\n"
entry = "## [0.25.0] - 2026-07-15\n\n- new\n"
result = re._insert_changelog_entry(existing, entry)
assert result.index("## [0.25.0]") < result.index("## [0.24.0]")
assert "- old" in result and "- new" in result
@@ -13,6 +13,7 @@ import pytest
from roboco.services.release_readiness import (
CommitInfo,
_commits_since,
_draft_changelog,
_run_git,
classify_changes,
derive_bump,
@@ -137,3 +138,29 @@ def test_commits_since_preserves_field_sep_in_body(
assert commit.subject == subject
assert commit.body == body # the embedded \x1f is preserved
assert commit.pr_number == 1
def test_draft_changelog_uses_curated_unreleased_body() -> None:
changes = classify_changes(
[CommitInfo(sha="a" * 8, subject="feat: shiny thing", body="")]
)
changelog = (
"# Changelog\n\n## [Unreleased]\n\n### Added\n\n"
"- **Shiny thing.** Curated prose about it.\n\n"
"## [0.24.0] - 2026-07-14\n\n- old\n"
)
draft = _draft_changelog("0.25.0", changes, "2026-07-15", changelog)
assert draft.startswith("## [0.25.0] - 2026-07-15")
assert "Curated prose about it." in draft
assert "feat: shiny thing" not in draft
assert "[Unreleased]" not in draft
def test_draft_changelog_falls_back_to_transcription_without_curation() -> None:
changes = classify_changes(
[CommitInfo(sha="a" * 8, subject="feat: shiny thing", body="")]
)
empty = "# Changelog\n\n## [Unreleased]\n\n## [0.24.0] - 2026-07-14\n"
draft = _draft_changelog("0.25.0", changes, "2026-07-15", empty)
assert "### Added" in draft
assert "- shiny thing" in draft