fix(release): keep the suggested Reddit title within its own limit

The previous commit bounded the post body but left the title unbounded, and
Reddit caps titles separately and far tighter. Five highlights at the
validated 60-character maximum produce a 328-character title against the
300-character cap, while the body stays nowhere near its own limit — so the
renderer returned a draft that looked fine and could not be submitted.

The title now names as many highlights as fit and counts the rest.

195 tests passing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Fable 5 committed 2026-08-27 11:02:49 +02:00
1 parent af6d102776
commit f7cafc50f4
3 files changed
+77 -6

No files matched your search

+5 -2
View File
@@ -83,8 +83,11 @@ Highlights drive three behaviors:
longer post. If the breaking changes alone cannot fit the 4096-character
limit, the render fails with an actionable error rather than dropping one.
- **Reddit** gives each one an `## Highlights` subsection, with the remaining
changes grouped below. Bounded the same way at Reddit's 40,000-character
post limit — this repository's accumulated notes already render ~37,000 —
changes grouped below. Its suggested title names as many highlights as
Reddit's separate 300-character title cap allows and counts the rest — five
highlights at the validated 60-character maximum already overshoot it while
the body stays nowhere near its own limit. The body is bounded the same way
at Reddit's 40,000-character post limit — this repository's accumulated notes already render ~37,000 —
dropping from the tail of the grouped list, which is ordered breaking →
feature → fix → perf so the least consequential go first. A breaking change
is never dropped there either.
+40 -4
View File
@@ -27,6 +27,45 @@ export const TELEGRAM_MESSAGE_LIMIT = 4096;
*/
export const REDDIT_POST_LIMIT = 40000;
/**
* Reddit caps a post title separately from its body, and far tighter: five
* highlights at the validated 60-character maximum already overshoot it while
* the body stays nowhere near its own limit.
*/
export const REDDIT_TITLE_LIMIT = 300;
/**
* Names as many highlights as the title limit allows, counting the rest.
*
* @param {string} version
* @param {object[]} highlights
* @returns {string}
*/
export function buildRedditTitle(version, highlights) {
const prefix = `IPTVnator v${version}`;
if (highlights.length === 0) {
return `${prefix} released`;
}
const names = highlights.map((note) => note.highlight);
for (let visible = names.length; visible > 0; visible -= 1) {
const hidden = names.length - visible;
const title = `${prefix} — ${names.slice(0, visible).join(', ')}${
hidden > 0 ? `, and ${hidden} more` : ''
}`;
if (title.length <= REDDIT_TITLE_LIMIT) {
return title;
}
}
// Unreachable while the highlight cap stays well under the title limit,
// but a title is never worth failing a release over.
return `${prefix} released`;
}
const TYPE_EMOJI = {
breaking: '⚠️',
feature: '✨',
@@ -179,10 +218,7 @@ export function renderRedditPost(notes, { version }) {
return null;
}
const title =
highlights.length > 0
? `IPTVnator v${version} — ${highlights.map((note) => note.highlight).join(', ')}`
: `IPTVnator v${version} released`;
const title = buildRedditTitle(version, highlights);
const buildPost = (visibleRest, droppedCount) => {
const blocks = [`Suggested title: ${title}`, '---'];
@@ -2,7 +2,9 @@ import assert from 'node:assert/strict';
import { describe, it } from 'node:test';
import {
buildRedditTitle,
REDDIT_POST_LIMIT,
REDDIT_TITLE_LIMIT,
renderRedditPost,
renderTelegramPost,
splitHighlights,
@@ -235,6 +237,36 @@ describe('renderRedditPost', () => {
assert.doesNotMatch(post, /Internal churn/);
});
it('keeps the suggested title inside the Reddit title limit', () => {
// Five highlights at the validated 60-character maximum overshoot the
// 300-character title cap while the body stays far below its own.
const notes = Array.from({ length: 5 }, (_, index) =>
note({
highlight: `H${index}`.padEnd(60, 'x'),
sourcePath: `.changes/feature-${index}.md`,
})
);
const post = renderRedditPost(notes, { version: '0.24.0' });
const title = post.split('\n')[0].replace('Suggested title: ', '');
assert.ok(
title.length <= REDDIT_TITLE_LIMIT,
`title is ${title.length} characters`
);
assert.match(title, /, and \d+ more$/);
});
it('names every highlight when they fit', () => {
assert.equal(
buildRedditTitle('0.24.0', [
{ highlight: 'Up Next rail' },
{ highlight: 'Faster imports' },
]),
'IPTVnator v0.24.0 — Up Next rail, Faster imports'
);
assert.equal(buildRedditTitle('0.24.0', []), 'IPTVnator v0.24.0 released');
});
it('stays inside the Reddit post limit and says what it dropped', () => {
const notes = [
note({ highlight: 'Up Next rail' }),