openvibers/openvibe.blog Commits
Patch notes: the day ends each title ('Patch notes: 80 changes across 36 sites' was three different posts' title) (#65)
Changes
@@@ -244,7 +244,11 @@ function createChangelog({ config, store, blogs, posts, aiDrafts = null, fetchImconst to = day(list[list.length - 1].deployed_at);const sites = order.map(name);const lead = highlights[0] ? clean(highlights[0].subject.split(/[:.;(]/)[0], 70) : null;-- const title = lead && lead.length >= 12 ? `Patch notes: ${lead}` : `Patch notes: ${list.length} changes across ${order.length} ${order.length === 1 ? 'site' : 'sites'}`;++ // The day ends the title: titles made from the counts repeated ("Patch notes: 80 changes across 36 sites" three++ // times by 2026-10-17), which search results and the feed cannot tell apart. The "Patch notes: " start stays++ // (openvibe-shared/shipped.js strips it to show the rest).++ const dayLabel = new Date(list[list.length - 1].deployed_at).toLocaleDateString('en-US', { month: 'short', day: 'numeric', timeZone: 'UTC' });++ const title = `Patch notes: ${lead && lead.length >= 12 ? lead : `${list.length} changes across ${order.length} ${order.length === 1 ? 'site' : 'sites'}`} (${dayLabel})`;const summary = clean(`What shipped on OpenVibe ${from === to ? `on ${from}` : `from ${from} to ${to}`}: ${list.length} ${list.length === 1 ? 'change' : 'changes'} to ${sites.slice(0, 6).join(', ')}${sites.length > 6 ? ` and ${sites.length - 6} more` : ''}.`, 400);const out = [`${summary} Every line below is a commit message from the OpenVibers repositories, linked to the change itself.`, ''];if (highlights.length) {@@@ -65,7 +65,7 @@ const H = (n) => n.toString(16).padStart(40, 'a');const post = await t.ctx.posts.get(r.post.id);assert.strictEqual(post.state, 'published');const head = await t.ctx.posts.head(post);-- assert.strictEqual(head.fields.title, 'Patch notes: Recent tools in the launcher');++ assert.match(head.fields.title, /^Patch notes: Recent tools in the launcher \([A-Z][a-z]{2} \d{1,2}\)$/, 'the day ends the title');assert.ok(head.content.includes('## Highlights') && head.content.includes('**OpenVibe.Tools:** Recent tools in the launcher'));assert.ok(head.content.includes('## OpenVibe.Live') && head.content.includes('Fix the chat scroll'));assert.strictEqual(head.meta.authorship.mode, 'imported');@@@ -80,7 +80,7 @@ const H = (n) => n.toString(16).padStart(40, 'a');assert.deepStrictEqual(j.entries.map((x) => x.subject).sort(), ['Recent tools in the launcher: anonymous and account history', 'Tidy the docs']);assert.ok(j.entries.every((x) => x.service === 'tools' && x.post_id));assert.ok(j.latest_post && /\/@openvibe\//.test(j.latest_post.url), 'the latest patch notes post, to link to');-- assert.strictEqual(j.latest_post.title, 'Patch notes: Recent tools in the launcher');++ assert.match(j.latest_post.title, /^Patch notes: Recent tools in the launcher \([A-Z][a-z]{2} \d{1,2}\)$/);});await check('a batch publishes without a major, but never sooner than the gap after the last post', async () => {@@@ -90,7 +90,7 @@ const H = (n) => n.toString(16).padStart(40, 'a');t.clock.advance(6 * 3600 * 1000 + 1000);const r = await cl.tick();assert.ok(r && r.post);-- assert.ok((await t.ctx.posts.head(await t.ctx.posts.get(r.post.id))).fields.title.startsWith('Patch notes: 5 changes across 1 site'));++ assert.ok((await t.ctx.posts.head(await t.ctx.posts.get(r.post.id))).fields.title.match(/^Patch notes: 5 changes across 1 site \([A-Z][a-z]{2} \d{1,2}\)$/));});await check('subjects are cut at a word; version bumps count as major', async () => {
@@ -244,7 +244,11 @@ function createChangelog({ config, store, blogs, posts, aiDrafts = null, fetchIm
@@ -244,7 +244,11 @@ function createChangelog({ config, store, blogs, posts, aiDrafts = null, fetchIm
const to = day(list[list.length - 1].deployed_at);
const to = day(list[list.length - 1].deployed_at);
const sites = order.map(name);
const sites = order.map(name);
const lead = highlights[0] ? clean(highlights[0].subject.split(/[:.;(]/)[0], 70) : null;
const lead = highlights[0] ? clean(highlights[0].subject.split(/[:.;(]/)[0], 70) : null;
- const title = lead && lead.length >= 12 ? `Patch notes: ${lead}` : `Patch notes: ${list.length} changes across ${order.length} ${order.length === 1 ? 'site' : 'sites'}`;
+ // The day ends the title: titles made from the counts repeated ("Patch notes: 80 changes across 36 sites" three
+ // times by 2026-10-17), which search results and the feed cannot tell apart. The "Patch notes: " start stays
+ // (openvibe-shared/shipped.js strips it to show the rest).
+ const dayLabel = new Date(list[list.length - 1].deployed_at).toLocaleDateString('en-US', { month: 'short', day: 'numeric', timeZone: 'UTC' });
+ const title = `Patch notes: ${lead && lead.length >= 12 ? lead : `${list.length} changes across ${order.length} ${order.length === 1 ? 'site' : 'sites'}`} (${dayLabel})`;
const summary = clean(`What shipped on OpenVibe ${from === to ? `on ${from}` : `from ${from} to ${to}`}: ${list.length} ${list.length === 1 ? 'change' : 'changes'} to ${sites.slice(0, 6).join(', ')}${sites.length > 6 ? ` and ${sites.length - 6} more` : ''}.`, 400);
const summary = clean(`What shipped on OpenVibe ${from === to ? `on ${from}` : `from ${from} to ${to}`}: ${list.length} ${list.length === 1 ? 'change' : 'changes'} to ${sites.slice(0, 6).join(', ')}${sites.length > 6 ? ` and ${sites.length - 6} more` : ''}.`, 400);
const out = [`${summary} Every line below is a commit message from the OpenVibers repositories, linked to the change itself.`, ''];
const out = [`${summary} Every line below is a commit message from the OpenVibers repositories, linked to the change itself.`, ''];
if (highlights.length) {
if (highlights.length) {
@@ -65,7 +65,7 @@ const H = (n) => n.toString(16).padStart(40, 'a');
@@ -65,7 +65,7 @@ const H = (n) => n.toString(16).padStart(40, 'a');
const post = await t.ctx.posts.get(r.post.id);
const post = await t.ctx.posts.get(r.post.id);
assert.strictEqual(post.state, 'published');
assert.strictEqual(post.state, 'published');
const head = await t.ctx.posts.head(post);
const head = await t.ctx.posts.head(post);
- assert.strictEqual(head.fields.title, 'Patch notes: Recent tools in the launcher');
+ assert.match(head.fields.title, /^Patch notes: Recent tools in the launcher \([A-Z][a-z]{2} \d{1,2}\)$/, 'the day ends the title');
assert.ok(head.content.includes('## Highlights') && head.content.includes('**OpenVibe.Tools:** Recent tools in the launcher'));
assert.ok(head.content.includes('## Highlights') && head.content.includes('**OpenVibe.Tools:** Recent tools in the launcher'));
assert.ok(head.content.includes('## OpenVibe.Live') && head.content.includes('Fix the chat scroll'));
assert.ok(head.content.includes('## OpenVibe.Live') && head.content.includes('Fix the chat scroll'));
assert.strictEqual(head.meta.authorship.mode, 'imported');
assert.strictEqual(head.meta.authorship.mode, 'imported');
@@ -80,7 +80,7 @@ const H = (n) => n.toString(16).padStart(40, 'a');
@@ -80,7 +80,7 @@ const H = (n) => n.toString(16).padStart(40, 'a');
assert.deepStrictEqual(j.entries.map((x) => x.subject).sort(), ['Recent tools in the launcher: anonymous and account history', 'Tidy the docs']);
assert.deepStrictEqual(j.entries.map((x) => x.subject).sort(), ['Recent tools in the launcher: anonymous and account history', 'Tidy the docs']);
assert.ok(j.entries.every((x) => x.service === 'tools' && x.post_id));
assert.ok(j.entries.every((x) => x.service === 'tools' && x.post_id));
assert.ok(j.latest_post && /\/@openvibe\//.test(j.latest_post.url), 'the latest patch notes post, to link to');
assert.ok(j.latest_post && /\/@openvibe\//.test(j.latest_post.url), 'the latest patch notes post, to link to');
- assert.strictEqual(j.latest_post.title, 'Patch notes: Recent tools in the launcher');
+ assert.match(j.latest_post.title, /^Patch notes: Recent tools in the launcher \([A-Z][a-z]{2} \d{1,2}\)$/);
});
});
await check('a batch publishes without a major, but never sooner than the gap after the last post', async () => {
await check('a batch publishes without a major, but never sooner than the gap after the last post', async () => {
@@ -90,7 +90,7 @@ const H = (n) => n.toString(16).padStart(40, 'a');
@@ -90,7 +90,7 @@ const H = (n) => n.toString(16).padStart(40, 'a');
t.clock.advance(6 * 3600 * 1000 + 1000);
t.clock.advance(6 * 3600 * 1000 + 1000);
const r = await cl.tick();
const r = await cl.tick();
assert.ok(r && r.post);
assert.ok(r && r.post);
- assert.ok((await t.ctx.posts.head(await t.ctx.posts.get(r.post.id))).fields.title.startsWith('Patch notes: 5 changes across 1 site'));
+ assert.ok((await t.ctx.posts.head(await t.ctx.posts.get(r.post.id))).fields.title.match(/^Patch notes: 5 changes across 1 site \([A-Z][a-z]{2} \d{1,2}\)$/));
});
});
await check('subjects are cut at a word; version bumps count as major', async () => {
await check('subjects are cut at a word; version bumps count as major', async () => {