recover-pending-cut fold-back silently drops [Unreleased] text outside an accepted ### heading #1491

Closed
opened 2026-10-02 10:06:35 +02:00 by claude · 1 comment
Member

rt recover-pending-cut (recovery B) folds a stranded section back under ## [Unreleased] with changelog.FoldBackIntoUnreleased (internal/changelog/foldback.go). When [Unreleased] is already non-empty, it merges the two bodies through changelog.MergeSections. That merge drops every line that is not under an accepted ### <Kind> heading. It is the same mechanism #1486 makes rt prep refuse.

Measured

Go probe calling FoldBackIntoUnreleased(cl, "0.2.0") on the #1486 tree, 2026-10-02. foldback.go there is unchanged from main @ 02fae6a.

[Unreleased] before:

New prose after the prepare.

### Notes

- A note.

Pending section [v0.2.0]:

### Added

- Shipped.

Result: err=nil, and [Unreleased] after is:

### Added

- Shipped.

The prose and the whole ### Notes block are gone. The empty-[Unreleased] splice path is not affected: it carries the section's lines verbatim.

Why it matters

The deleted text never reaches the next rt prep, so #1486's refusal cannot catch it: prep only sees what recovery left behind. An operator who runs the documented recovery loses hand-written notes, and the verb exits 0.

Acceptance criteria

  • With a non-empty [Unreleased], recovery refuses and writes nothing when changelog.UncarriedLines reports any line of the [Unreleased] body. The refusal names each line and the accepted headings (changelog.MergeKinds), as rt prep does after #1486. — DONE @ #1493 (2060ed1, reviewed as 2060ed1)
  • The pending section's body is checked too, or the PR records why not. It is MergeSections' second argument, so the current kind carries over from [Unreleased], and UncarriedLines is documented as exact only for the first argument. — DONE @ #1493 (2060ed1, reviewed as 2060ed1). It is checked on its own. Lines above its first heading are refused too, and the refusal says whether the merge would move or drop each one. #1493's decision tree records why.
  • The empty-[Unreleased] splice path stays byte-identical. — DONE @ #1493 (2060ed1, reviewed as 2060ed1)
  • Tests are red on main and mutation-verified (AGENTS.md section 8, Axis B). — DONE @ #1493 (2060ed1, reviewed as 2060ed1). The tests were red on the pre-fix tree, with a behaviour-free stub of the new type: they reference FoldBackDropError, which #1493 adds, so they do not compile on the old main. Mutation runs: 19/19 killed at 0935704, and 5/5 for the verb at 2060ed1.

Notes

  • Depends on #1486 for UncarriedLines and MergeKinds.
  • #1490 (open, for #1487) reworks this function into FoldBackIntoUnreleasedExcept. Not checked: whether its diff changes the merge arm. Whichever lands second rebases onto the other.
    • Checked in #1493: #1490 leaves the merge arm alone but filters the pending lines (subtractCovered). Since #1493, both bodies are []fileRecord, and the grading reads the same records the merge is fed. Rebasing #1490 only needs subtractCovered to filter records.
  • Raised in the review of #1486 (note 3).
`rt recover-pending-cut` (recovery B) folds a stranded section back under `## [Unreleased]` with `changelog.FoldBackIntoUnreleased` (`internal/changelog/foldback.go`). When `[Unreleased]` is already non-empty, it merges the two bodies through `changelog.MergeSections`. That merge drops every line that is not under an accepted `### <Kind>` heading. It is the same mechanism #1486 makes `rt prep` refuse. ## Measured Go probe calling `FoldBackIntoUnreleased(cl, "0.2.0")` on the #1486 tree, 2026-10-02. `foldback.go` there is unchanged from `main` @ `02fae6a`. `[Unreleased]` before: ``` New prose after the prepare. ### Notes - A note. ``` Pending section `[v0.2.0]`: ``` ### Added - Shipped. ``` Result: `err=nil`, and `[Unreleased]` after is: ``` ### Added - Shipped. ``` The prose and the whole `### Notes` block are gone. The empty-`[Unreleased]` splice path is not affected: it carries the section's lines verbatim. ## Why it matters The deleted text never reaches the next `rt prep`, so #1486's refusal cannot catch it: prep only sees what recovery left behind. An operator who runs the documented recovery loses hand-written notes, and the verb exits 0. ## Acceptance criteria - [x] With a non-empty `[Unreleased]`, recovery refuses and writes nothing when `changelog.UncarriedLines` reports any line of the `[Unreleased]` body. The refusal names each line and the accepted headings (`changelog.MergeKinds`), as `rt prep` does after #1486. — DONE @ #1493 (`2060ed1`, reviewed as `2060ed1`) - [x] The pending section's body is checked too, or the PR records why not. It is `MergeSections`' second argument, so the current kind carries over from `[Unreleased]`, and `UncarriedLines` is documented as exact only for the first argument. — DONE @ #1493 (`2060ed1`, reviewed as `2060ed1`). It is checked on its own. Lines above its first heading are refused too, and the refusal says whether the merge would move or drop each one. #1493's decision tree records why. - [x] The empty-`[Unreleased]` splice path stays byte-identical. — DONE @ #1493 (`2060ed1`, reviewed as `2060ed1`) - [x] Tests are red on `main` and mutation-verified (AGENTS.md section 8, Axis B). — DONE @ #1493 (`2060ed1`, reviewed as `2060ed1`). The tests were red on the pre-fix tree, with a behaviour-free stub of the new type: they reference `FoldBackDropError`, which #1493 adds, so they do not compile on the old `main`. Mutation runs: 19/19 killed at `0935704`, and 5/5 for the verb at `2060ed1`. ## Notes - Depends on #1486 for `UncarriedLines` and `MergeKinds`. - #1490 (open, for #1487) reworks this function into `FoldBackIntoUnreleasedExcept`. Not checked: whether its diff changes the merge arm. Whichever lands second rebases onto the other. - Checked in #1493: #1490 leaves the merge arm alone but filters the pending lines (`subtractCovered`). Since #1493, both bodies are `[]fileRecord`, and the grading reads the same records the merge is fed. Rebasing #1490 only needs `subtractCovered` to filter records. - Raised in the review of #1486 (note 3).
Author
Member

Landed on main as 2060ed13ffb3834928b2aad3a32916eab485acb3 via #1493 (rebase merge, 2026-10-02 12:20:51).

  • Landing vs review. It was a fast-forward: ff3345f, 0935704 and 2060ed1 landed with their reviewed SHAs. git range-diff 55a12b9..2060ed1 55a12b9..origin/main shows = for all three, and the trees match.
  • Approval. alex, review 7161: APPROVED, official, bound to 2060ed1, the merged head. For the record, it was submitted at 12:14:41. That is inside the HOLD window for the re-review 7160 nit (HOLD comment 113878 at 12:11:01, released by 113882 at 12:16:12), so it is outside the AGENTS.md section 6.2 order. The approved SHA is the SHA that landed.
  • Other reviews. Claude reviews 7158 (at ff3345f) and 7160 (at 0935704) were posted as COMMENT, because that account authored the PR. The reviewing session reports verifying 2060ed1 after the merge.
  • Checks. PR CI on 2060ed1: 30/30 success. push:main on 2060ed1: 15 of 16 success. go-ci / record reviewed vs landed commit (push) failed with comment page 2 repeated the previous non-empty page. That is the recorder defect tracked in #1495: it is red on every commented landing since #1486, and it is not this change.
  • Branch. claude/1491-foldback-refuse-drop was deleted after the range-diff check.
  • Follow-up. #1507 covers the same raw \r in rt prep's refusal (prep.UnreleasedDropError), measured on 2060ed1.

All four acceptance criteria are ticked above (DONE @ #1493). Closing by hand, because the PR used Refs.

Landed on `main` as `2060ed13ffb3834928b2aad3a32916eab485acb3` via #1493 (rebase merge, 2026-10-02 12:20:51). - **Landing vs review.** It was a fast-forward: `ff3345f`, `0935704` and `2060ed1` landed with their reviewed SHAs. `git range-diff 55a12b9..2060ed1 55a12b9..origin/main` shows `=` for all three, and the trees match. - **Approval.** alex, review 7161: APPROVED, official, bound to `2060ed1`, the merged head. For the record, it was submitted at 12:14:41. That is inside the HOLD window for the re-review 7160 nit (HOLD comment 113878 at 12:11:01, released by 113882 at 12:16:12), so it is outside the AGENTS.md section 6.2 order. The approved SHA is the SHA that landed. - **Other reviews.** Claude reviews 7158 (at `ff3345f`) and 7160 (at `0935704`) were posted as COMMENT, because that account authored the PR. The reviewing session reports verifying `2060ed1` after the merge. - **Checks.** PR CI on `2060ed1`: 30/30 success. push:main on `2060ed1`: 15 of 16 success. `go-ci / record reviewed vs landed commit (push)` failed with `comment page 2 repeated the previous non-empty page`. That is the recorder defect tracked in #1495: it is red on every commented landing since #1486, and it is not this change. - **Branch.** `claude/1491-foldback-refuse-drop` was deleted after the range-diff check. - **Follow-up.** #1507 covers the same raw `\r` in `rt prep`'s refusal (`prep.UnreleasedDropError`), measured on `2060ed1`. All four acceptance criteria are ticked above (DONE @ #1493). Closing by hand, because the PR used `Refs`.
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#1491
No description provided.