Skip to content

fix(orchestrator): auto-truncate release PR body when it exceeds provider limit#38

Merged
theogravity merged 2 commits into
mainfrom
fix/release-pr-body-limit
May 2, 2026
Merged

fix(orchestrator): auto-truncate release PR body when it exceeds provider limit#38
theogravity merged 2 commits into
mainfrom
fix/release-pr-body-limit

Conversation

@theogravity

Copy link
Copy Markdown
Contributor

Summary

Closes #37. The always-open release PR's body now auto-truncates when its rendered length would exceed the provider's PR-body limit (65,536 chars on GitHub / Gitea, conservative bound on GitLab). Replaces a hard 422 Validation Failed: body is too long with a graceful fallback to a compact form.

What changed

  • internal/release/render.go: new RenderPreviewCompact function. Same header + version table + consumed footer as RenderPreview, but the per-package "Released changes" sections are elided with a pointer to each package's CHANGELOG.md.
  • internal/orchestrator/orchestrator.go: new MaxPRBodyBytes constant (65536) and renderBodyWithinLimit helper. Three-tier fallback: full → compact → hard byte-truncation with a trailing marker.

Repro

The loglayer-go v2 cascade (loglayer/loglayer-go#61) shipped 27 packages at :major from a single multi-package changeset whose body was ~3.4 KB. 27 × 3.4 KB + table overhead = ~92 KB rendered, well over GitHub's 65,536-char cap. The release-pr workflow failed with the 422 error.

After this PR, the same plan would render compact (~2 KB) and the workflow would succeed.

Test plan

  • go test -count=1 ./... passes.
  • New TestRenderPreviewCompact (verifies compact form preserves the table + footer, drops the per-package sections + changeset body).
  • New TestRenderPreviewCompact_EmptyPlan (empty / nil plans still render the no-pending-changesets message).
  • New TestRun_LongBodyFallsBackToCompact (synthetic 30-package × 5 KB body plan; verifies orchestrator falls back to compact and resulting body fits within MaxPRBodyBytes).
  • After merge, the loglayer-go v2 release-pr workflow re-runs against the now-compact-friendly orchestrator and opens the always-open PR successfully.

🤖 Generated with Claude Code

…ider limit

Adds release.RenderPreviewCompact (header + version table + footer,
no per-package sections) and a renderBodyWithinLimit fallback in
the orchestrator that picks the rendering size that fits.

Three-tier fallback:
1. Full RenderPreview (the default; full release notes per package).
2. RenderPreviewCompact (when full body > MaxPRBodyBytes = 65536).
3. Hard byte-truncation with a trailing marker (last-resort safety
   net for releases with hundreds of packages where even the
   compact form blows past the limit).

GitHub PR descriptions cap at 65,536 bytes; Gitea matches; GitLab
allows ~1 MiB. We use the conservative GitHub value across all
providers so a release plan that fits one provider fits all.

Tests:
- TestRenderPreviewCompact: verifies the compact form preserves
  the table, drops Released changes, drops the changeset body,
  keeps the consumed footer.
- TestRenderPreviewCompact_EmptyPlan: nil and empty plan still
  produce the no-pending-changesets message.
- TestRun_LongBodyFallsBackToCompact: builds a synthetic plan
  with 30 packages × 5KB changeset bodies (~150KB rendered) and
  verifies the orchestrator falls back to compact and the
  resulting body fits within MaxPRBodyBytes.

Fixes #37.
Important:
- Drop the unused today parameter from RenderPreviewCompact; the
  compact form has no per-package date-stamped CHANGELOG content,
  so today is dead weight on the signature. Update the call site
  in orchestrator + the existing tests.
- Add a hard-truncation test (TestTruncateWithMarker_Tier3). The
  test directly exercises the tier-3 path with a small max so we
  don't have to build a release plan large enough to drive the
  orchestrator there organically. Covers the multi-byte rune case
  that motivated the UTF-8 boundary walk-back.

Suggestions:
- Walk back to a UTF-8 codepoint boundary on the hard-truncate
  path so a multi-byte rune never splits and renders as a U+FFFD
  replacement character on GitHub. Cheap to add; just imports
  unicode/utf8 and adds 3 lines.
- Export the elided-notes marker as release.CompactElidedNotesMarker
  so downstream tooling that scrapes release-PR bodies has a stable
  detection target.
- Extract truncation into a private truncateWithMarker helper and
  expose it via export_test.go so the test suite gets direct
  coverage of the bounds + UTF-8 logic without needing a synthetic
  200-package fake plan.
@theogravity theogravity enabled auto-merge (squash) May 2, 2026 23:29
@theogravity theogravity merged commit 8908d2c into main May 2, 2026
6 checks passed
@theogravity theogravity deleted the fix/release-pr-body-limit branch May 2, 2026 23:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

release-pr workflow fails closed when rendered PR body exceeds GitHub's 65,536-char limit

1 participant