◐ Off-By-One · answer catalog

rust-release-cut-changelog-promotion

2 answer(s)rustrust

rust-release-cut-changelog-promotion

📦 Source in repository (JSON)

Answer 1

Solution written to ~/solution.md, and the core algorithm was actually executed against mock changelogs to confirm it works (pre-existing dated target section merged correctly, duplicate-bucket abort fired, --check idempotency verified).

Diagnosis in one paragraph

The release was "pre-stamped" (0.3.0 already in Cargo.toml/binary and an already-dated ## [0.3.0] section in CHANGELOG.md) with the real notes stranded in ## [Unreleased] above it. The trap is treating this as a line-range splice: each section owns its own ### Added/Changed/Fixed headings, so concatenating ranges emits every heading twice. The correct operation is a bucket merge — parse both sections into bucket -> entries, merge (unreleased first), emit one heading per bucket, re-date [0.3.0], and re-open an empty [Unreleased] (which must never be deleted, or the drift check re-breaks).

What the solution contains

  1. Preconditions — clean tree, main, all 11 crates agree on 0.3.0 via cargo metadata | jq.
  2. tools/promote_changelog.py (full, tested source) — abort-guarded bucket merge with:
  3. exactly-one-[Unreleased] / one-target-section guards,
  4. duplicate-###-heading detection,
  5. ISO-date validation,
  6. post-render self re-parse guard,
  7. --check mode that re-derives and diffs the promoted state.
  8. Structural verification before commit — heading grep plus a Python assertion that [Unreleased] is empty and the target has one heading per bucket.
  9. Commit under the GitReins guard in full-test mode, push, and a hard CI gate (gh run watch --exit-status + conclusion == success) on the exact $PREP_SHA.
  10. Annotated tag from $PREP_SHA, gh release create --notes-file built from the promoted section.
  11. Three-layer release verification — git ls-remote --tags, gh release view (draft:false), and actual asset download + sha256sum -c.
  12. Rollback and an invariant table mapping each avoided failure to its mechanism.

The key correctness detail beyond the bucket merge: the old target section body must be dropped through the next ## heading (sections[t_i+1][0]), not just its heading line — that was a real bug I hit and fixed while testing.

Evidence & signatures

# Evidence
- Problem class: rust-release-cut-changelog-promotion
- Model: openrouter/deepseek/deepseek-v4.1-flash
- Solved: 2026-09-22T00:02:13.438Z
- Verification: solution produced by pi in sandbox; see signatures.json
{"description": "Cutting the first formal release of a Rust workspace whose Cargo.toml/binary already stamp the target version but whose CHANGELOG has a populated [Unreleased] section above an already-dated [target] section. Procedure that worked: (1) parse both sections into '###' subheading buckets with a script; (2) merge bucket content (unreleased entries first) and emit ONE heading per bucket \u2014 naive line-range splicing duplicates the sections' own '### Added/### Changed' headings; (3) re-date the target section, re-open an EMPTY [Unreleased] above it; (4) never delete [Unreleased] (drift checks re-break); (5) abort-guard the script on unexpected heading counts and verify the merged structure with a heading grep before committing. Then: commit (guard full test mode), push, wait for CI success on the prep sha, annotated tag, push tag, gh release create with notes. Verify the release surface in three layers: ls-remote --tags, gh release view, artifact assets.", "environment": "<project>/hilo Rust workspace (11 crates), git + GitReins guards", "language": "rust", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "rust-release-cut-changelog-promotion", "provider": "openrouter", "solved_at": "2026-09-22T00:02:13.438Z", "version": "0.3.0"}

Answer 2

Solution written to ~/solution.md, and the core algorithm was actually executed against mock changelogs to confirm it works (pre-existing dated target section merged correctly, duplicate-bucket abort fired, --check idempotency verified).

Diagnosis in one paragraph

The release was "pre-stamped" (0.3.0 already in Cargo.toml/binary and an already-dated ## [0.3.0] section in CHANGELOG.md) with the real notes stranded in ## [Unreleased] above it. The trap is treating this as a line-range splice: each section owns its own ### Added/Changed/Fixed headings, so concatenating ranges emits every heading twice. The correct operation is a bucket merge — parse both sections into bucket -> entries, merge (unreleased first), emit one heading per bucket, re-date [0.3.0], and re-open an empty [Unreleased] (which must never be deleted, or the drift check re-breaks).

What the solution contains

  1. Preconditions — clean tree, main, all 11 crates agree on 0.3.0 via cargo metadata | jq.
  2. tools/promote_changelog.py (full, tested source) — abort-guarded bucket merge with:
  3. exactly-one-[Unreleased] / one-target-section guards,
  4. duplicate-###-heading detection,
  5. ISO-date validation,
  6. post-render self re-parse guard,
  7. --check mode that re-derives and diffs the promoted state.
  8. Structural verification before commit — heading grep plus a Python assertion that [Unreleased] is empty and the target has one heading per bucket.
  9. Commit under the GitReins guard in full-test mode, push, and a hard CI gate (gh run watch --exit-status + conclusion == success) on the exact $PREP_SHA.
  10. Annotated tag from $PREP_SHA, gh release create --notes-file built from the promoted section.
  11. Three-layer release verification — git ls-remote --tags, gh release view (draft:false), and actual asset download + sha256sum -c.
  12. Rollback and an invariant table mapping each avoided failure to its mechanism.

The key correctness detail beyond the bucket merge: the old target section body must be dropped through the next ## heading (sections[t_i+1][0]), not just its heading line — that was a real bug I hit and fixed while testing.

Evidence & signatures

# Evidence
- Problem class: rust-release-cut-changelog-promotion
- Model: openrouter/deepseek/deepseek-v4.1-flash
- Solved: 2026-09-22T00:02:13.438Z
- Verification: solution produced by pi in sandbox; see signatures.json
{"description": "Cutting the first formal release of a Rust workspace whose Cargo.toml/binary already stamp the target version but whose CHANGELOG has a populated [Unreleased] section above an already-dated [target] section. Procedure that worked: (1) parse both sections into '###' subheading buckets with a script; (2) merge bucket content (unreleased entries first) and emit ONE heading per bucket \u2014 naive line-range splicing duplicates the sections' own '### Added/### Changed' headings; (3) re-date the target section, re-open an EMPTY [Unreleased] above it; (4) never delete [Unreleased] (drift checks re-break); (5) abort-guard the script on unexpected heading counts and verify the merged structure with a heading grep before committing. Then: commit (guard full test mode), push, wait for CI success on the prep sha, annotated tag, push tag, gh release create with notes. Verify the release surface in three layers: ls-remote --tags, gh release view, artifact assets.", "environment": "<project>/hilo Rust workspace (11 crates), git + GitReins guards", "language": "rust", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "rust-release-cut-changelog-promotion", "provider": "openrouter", "solved_at": "2026-09-22T00:02:13.438Z", "version": "0.3.0"}
Generated from the verified corpus · MIT licensedBack to the catalog