openvibers/openvibe.live Commits

The Today card has a version: an older stored card is regenerated (#832)

OP OpenVibers committed 2 days ago · 5914c70a1f · 2 files, +11 −8

A card written by the first prompt said there were no takes on a day with one. The card version is
now part of the stored shape; get() ignores an older card, so the next tick writes a fresh one
instead of serving the stale words for the rest of the day.

Checked: ov test test/rundown-today.test.js.

Changes

ddiff --git a/server/rundown/today.js b/server/rundown/today.js
iindex b8a4c8c1..4e7c9b90 100644
---- a/server/rundown/today.js
++++ b/server/rundown/today.js
@@@ -28,6 +28,7 @@ const CHECK_MS = 5 * 60 * 1000; // how often the job looks for a due day
const MIN_TAKES = 1; // one take is a day: the card says what was said, and grows with the day
const MAX_TAKES = 30; // the workflow takes at most 30 takes
const MAX_FETCH = 400; // a very busy day is sampled, not read whole
++const CARD_VERSION = 2; // 2: the day's takes are listed for the model, and a card that denies them is dropped
const KEY = (date) => `rundown_today:${date}`;
const utcDay = (now = Date.now()) => new Date(now).toISOString().slice(0, 10);
@@@ -78,7 +79,7 @@ function cardInput(date, items) {
*/
function isDue(stored, { now = Date.now(), total = 0, latest_take_id = null } = {}) {
if (Number(total) < MIN_TAKES) return false;
-- if (!stored || !Number(stored.generated_at)) return true;
++ if (!stored || stored.version !== CARD_VERSION || !Number(stored.generated_at)) return true;
if (now - Number(stored.generated_at) < REFRESH_MS) return false;
const seen = Number(stored.latest_take_id || 0);
return latest_take_id != null && Number(latest_take_id) > seen;
@@@ -126,7 +127,7 @@ async function get(date) {
try {
const raw = await db.getSetting(KEY(date));
const card = raw ? JSON.parse(raw) : null;
-- return card && card.status === 'ready' && card.date === date ? card : null;
++ return card && card.status === 'ready' && card.version === CARD_VERSION && card.date === date ? card : null;
} catch { return null; }
}
@@@ -157,7 +158,7 @@ async function refresh(date, { now = Date.now(), force = false, ai = null } = {}
catch { out = null; }
const text = cardText(out);
if (!text) return null;
-- const card = { status: 'ready', date, ...enrich(text, sample, total), generated_at: now, latest_take_id: latest, take_count: total, take_sample: sample.length };
++ const card = { status: 'ready', version: CARD_VERSION, date, ...enrich(text, sample, total), generated_at: now, latest_take_id: latest, take_count: total, take_sample: sample.length };
try { await db.setSetting(KEY(date), JSON.stringify(card)); } catch { return null; }
return card;
}
@@@ -195,5 +196,5 @@ function ssrHtml(card) {
module.exports = {
refresh, get, tick, start, stop, ssrHtml, isDue, sampleTakes, cardInput, cardText, takesFor, utcDay,
-- REFRESH_MS, CHECK_MS, MIN_TAKES, MAX_TAKES, KEY,
++ REFRESH_MS, CHECK_MS, MIN_TAKES, MAX_TAKES, CARD_VERSION, KEY,
};
ddiff --git a/test/rundown-today.test.js b/test/rundown-today.test.js
iindex 9000d7ed..74ea57c5 100644
---- a/test/rundown-today.test.js
++++ b/test/rundown-today.test.js
@@@ -15,10 +15,11 @@ const today = require('../server/rundown/today');
// ── Cadence: never before three takes, then 30 minutes apart with a new take ──
assert.strictEqual(today.isDue(null, { now, total: 0, latest_take_id: null }), false, 'a day with no takes has no card');
assert.strictEqual(today.isDue(null, { now, total: 1, latest_take_id: 9 }), true, 'the first take makes a card');
-- assert.strictEqual(today.isDue({ generated_at: now - 29 * 60000, latest_take_id: 9 }, { now, total: 10, latest_take_id: 10 }), false, 'not before 30 minutes');
-- assert.strictEqual(today.isDue({ generated_at: now - 31 * 60000, latest_take_id: 9 }, { now, total: 10, latest_take_id: 9 }), false, 'no new takes, no write');
-- assert.strictEqual(today.isDue({ generated_at: now - 31 * 60000, latest_take_id: 9 }, { now, total: 10, latest_take_id: 10 }), true, 'a new take after 30 minutes');
-- assert.strictEqual(today.isDue({ generated_at: now - 31 * 60000, latest_take_id: 9 }, { now, total: 10, latest_take_id: 8 }), false, 'an older cursor never rewrites');
++ assert.strictEqual(today.isDue({ version: today.CARD_VERSION, generated_at: now - 29 * 60000, latest_take_id: 9 }, { now, total: 10, latest_take_id: 10 }), false, 'not before 30 minutes');
++ assert.strictEqual(today.isDue({ version: today.CARD_VERSION, generated_at: now - 31 * 60000, latest_take_id: 9 }, { now, total: 10, latest_take_id: 9 }), false, 'no new takes, no write');
++ assert.strictEqual(today.isDue({ version: today.CARD_VERSION, generated_at: now - 31 * 60000, latest_take_id: 9 }, { now, total: 10, latest_take_id: 10 }), true, 'a new take after 30 minutes');
++ assert.strictEqual(today.isDue({ version: today.CARD_VERSION, generated_at: now - 31 * 60000, latest_take_id: 9 }, { now, total: 10, latest_take_id: 8 }), false, 'an older cursor never rewrites');
++ assert.strictEqual(today.isDue({ generated_at: now - 1000, latest_take_id: 9 }, { now, total: 10, latest_take_id: 9 }), true, 'a card from before this version is not a card');
// ── Sampling: first and last of the day, no duplicates, up to 30 ──
const items = Array.from({ length: 100 }, (_, i) => ({ id: i + 1, title: `take ${i + 1}` }));
@@@ -76,6 +77,7 @@ const today = require('../server/rundown/today');
}) });
assert.ok(card, 'the first card is written');
assert.strictEqual(card.status, 'ready');
++ assert.strictEqual(card.version, today.CARD_VERSION);
assert.strictEqual(card.take_count, 3);
assert.strictEqual(card.highlights.length, 1, 'only a real take becomes a highlight');
assert.deepStrictEqual(card.highlights[0], {