feat(release-prep): compile-time paragraph normalization for CHANGELOG fragments (v1.0.0 must-fix) #420

Closed
opened 2026-07-05 23:53:55 +02:00 by bosun · 0 comments
Owner

Empirical finding

PR#406 (v0.27.0 rolling release-prep) required manual paragraph unwrap by Herald because changelog.d/*.md fragments authored with hard-wraps at ~72 col carried those wraps through release-prep.sh's compile step into the compiled CHANGELOG.md section. Forgejo/Codeberg then rendered the hard-wraps as visible line-breaks (not word-wrap), producing choppy release notes + PR body prose.

Specific paragraphs required unwrap on PR#406:

  • ### Added Forgejo API resilience paragraph (#334)
  • ### Added publish_mode layered resolution paragraph (#408)
  • ### Fixed push-during-cut recovery paragraph (#417)

Broader concern

Two adopter authoring cultures exist for prose:

  • Prose-editor culture (vim/emacs/git-log style): 72-80 col hard-wrap is traditional
  • Modern markdown culture (Prettier/mdformat style): single-line per paragraph

release-toolkit currently forces adopters into the second culture by preserving hard-wraps through compile. Without a compile-time normalizer, every adopter who authors fragments with hard-wraps hits this same rendering issue in their release notes.

Operator directive 2026-07-05: v1.0.0 must-fix per class-completeness reasoning — the friendliest fix layer is the compile mechanism, not adopter discipline (option 1) or a PR-time gate error (options 3+4). Compile-time normalization means adopters can author however they like; the compiler produces render-clean output.

Fix path

Extend release-prep.sh's fragment-compile pass with a paragraph-unwrap transform. Reference implementation: Herald's manual transform on PR#406 (commit e8a078f) covers the essentials:

  • Within a paragraph (contiguous non-blank lines): join with single space
  • Preserve paragraph boundaries: blank lines between paragraphs remain blank
  • Preserve structural elements: list items (- , * , 1. ), fenced code blocks (```), inline code, blockquotes (> ), headers (#) NOT joined
  • Hyphen-split hazard guard: scan output for [a-z]- [a-z] pattern (hyphenated word split across former wrap boundaries → space-join fuses incorrectly). Rejoin the split.

Herald caught + fixed exactly one hyphen-split (pre-resilience) on PR#406 e8a078f — grep-guard for this pattern is essential.

Verification AC

  • Fragment authored with hard-wraps at any column produces single-line-per-paragraph output in compiled CHANGELOG.md
  • Fragment authored single-line per paragraph passes through unchanged (idempotency)
  • List items, code blocks, headers, blockquotes preserved verbatim
  • Hyphen-split hazard caught by output-scan grep + rejoined
  • Regression test suite: paragraph with hard-wraps → single line; list with hard-wrapped items → items unwrapped individually; code block with hard-wraps → preserved; mixed content → correct per-element handling
  • Herald's PR#406 e8a078f transform outputs match the automated transform's outputs on the same input fragments (empirical verification: run the new transform on pre-4d7ea41 CHANGELOG source, compare to Herald's e8a078f output)
  • Empirical anchor: PR#406 arc (dedup + #404-orphan clause + rendering unwrap)
  • Sibling class: Herald's feedback_release_prelude_rendering pin (hand-authored preludes need the same treatment; this generalizes to fragment-compiled paragraphs)
  • Sibling class: #403 axis-4 (commit-subject → CHANGELOG bypass) — both are class-completeness issues at the compile-into-CHANGELOG surface
  • Related: changelog-body-check.sh transition-window blind spot (Herald finding, in #403 body); worth considering whether the normalization + the transition-window fix land as one systematic fix at the compile boundary

Anchor

Operator ratified 2026-07-05 evening as v1.0.0 must-fix per class-completeness reasoning. Empirical anchor: PR#406 required manual per-cut cleanup by Herald. Without codified normalization, every adopter release hits this. QM's substrate lane (release-prep.sh); Herald's PR#406 e8a078f transform is directly reusable as reference implementation.

## Empirical finding **PR#406 (v0.27.0 rolling release-prep) required manual paragraph unwrap by Herald** because `changelog.d/*.md` fragments authored with hard-wraps at ~72 col carried those wraps through release-prep.sh's compile step into the compiled CHANGELOG.md section. Forgejo/Codeberg then rendered the hard-wraps as visible line-breaks (not word-wrap), producing choppy release notes + PR body prose. Specific paragraphs required unwrap on PR#406: - `### Added` Forgejo API resilience paragraph (#334) - `### Added` publish_mode layered resolution paragraph (#408) - `### Fixed` push-during-cut recovery paragraph (#417) ## Broader concern Two adopter authoring cultures exist for prose: - **Prose-editor culture** (vim/emacs/git-log style): 72-80 col hard-wrap is traditional - **Modern markdown culture** (Prettier/mdformat style): single-line per paragraph release-toolkit currently forces adopters into the second culture by preserving hard-wraps through compile. Without a compile-time normalizer, every adopter who authors fragments with hard-wraps hits this same rendering issue in their release notes. **Operator directive 2026-07-05: v1.0.0 must-fix** per class-completeness reasoning — the friendliest fix layer is the compile mechanism, not adopter discipline (option 1) or a PR-time gate error (options 3+4). Compile-time normalization means adopters can author however they like; the compiler produces render-clean output. ## Fix path Extend release-prep.sh's fragment-compile pass with a paragraph-unwrap transform. Reference implementation: Herald's manual transform on PR#406 (commit e8a078f) covers the essentials: - **Within a paragraph** (contiguous non-blank lines): join with single space - **Preserve paragraph boundaries**: blank lines between paragraphs remain blank - **Preserve structural elements**: list items (`- `, `* `, `1. `), fenced code blocks (```), inline code, blockquotes (`> `), headers (`#`) NOT joined - **Hyphen-split hazard guard**: scan output for `[a-z]- [a-z]` pattern (hyphenated word split across former wrap boundaries → space-join fuses incorrectly). Rejoin the split. Herald caught + fixed exactly one hyphen-split (`pre-resilience`) on PR#406 e8a078f — grep-guard for this pattern is essential. ## Verification AC - Fragment authored with hard-wraps at any column produces single-line-per-paragraph output in compiled CHANGELOG.md - Fragment authored single-line per paragraph passes through unchanged (idempotency) - List items, code blocks, headers, blockquotes preserved verbatim - Hyphen-split hazard caught by output-scan grep + rejoined - Regression test suite: paragraph with hard-wraps → single line; list with hard-wrapped items → items unwrapped individually; code block with hard-wraps → preserved; mixed content → correct per-element handling - Herald's PR#406 e8a078f transform outputs match the automated transform's outputs on the same input fragments (empirical verification: run the new transform on pre-4d7ea41 CHANGELOG source, compare to Herald's e8a078f output) ## Related - Empirical anchor: PR#406 arc (dedup + #404-orphan clause + rendering unwrap) - Sibling class: Herald's `feedback_release_prelude_rendering` pin (hand-authored preludes need the same treatment; this generalizes to fragment-compiled paragraphs) - Sibling class: #403 axis-4 (commit-subject → CHANGELOG bypass) — both are class-completeness issues at the compile-into-CHANGELOG surface - Related: `changelog-body-check.sh` transition-window blind spot (Herald finding, in #403 body); worth considering whether the normalization + the transition-window fix land as one systematic fix at the compile boundary ## Anchor Operator ratified 2026-07-05 evening as v1.0.0 must-fix per class-completeness reasoning. Empirical anchor: PR#406 required manual per-cut cleanup by Herald. Without codified normalization, every adopter release hits this. QM's substrate lane (release-prep.sh); Herald's PR#406 e8a078f transform is directly reusable as reference implementation.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
frankenbit/release-toolkit#420
No description provided.