S4 (a1): the media engine — VOICE templates, /desk, Ghost drafts

4a VOICE v1.1 committed (board start); lint is EXECUTABLE — banned list +
   no-exclamation law enforced in the engine (throws in test, drops in
   prod) and locked by tests. Curly-apostrophe variants covered.
4b mediaEngine: deterministic templates (MORNING WIRE, SIGNAL, STREAK
   WATCH, THE SETTLE, RECEIPTS, ARCHETYPE WATCH, LINE DISPATCH) filled
   ONLY from snapshot/ledger/streaks JSON. Record percentages never render
   under n>=20 (counts + 'Record building' below). Stark layer = curated
   committed library (content/stark-lines.json), day-rotated selection —
   selected, never generated.
4c /desk (founder-only: requireAuth + DESK_OWNERS email allowlist,
   deny-by-default): all formats as text + <=280-char pre-segmented tweets
   with per-tweet copy buttons + char counts, wire/numbers-only variants,
   DATA BRIEF block (structured day numbers) with copy-for-claude.ai.
   ntfy ping after the day's first snapshot: 'Desk pack ready'.
4d ghostPublisher: DRAFTS ONLY (status:'draft' test-locked), env-gated
   no-op, HS256 JWT via node crypto (zero new deps). POST
   /api/internal/ghost/drafts saves slate preview + settle drafts.
   Nothing anywhere auto-posts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Kev
2026-07-11 14:31:35 -04:00
parent d242b11b4b
commit 0e7871ac0e
12 changed files with 912 additions and 0 deletions
+179
View File
@@ -0,0 +1,179 @@
// Session 63 (A1-S4) — the VOICE v1.1 template engine. Numbers only from
// pipeline JSON; lint enforces the banned list + no-exclamation law; record
// claims obey n≥20; Stark lines are SELECTED from the committed library.
const media = require('../../src/services/mediaEngine');
const { lintVoice, segmentThread, starkLine, morningWire, signal, theSettle, receipt, streakWatch, archetypeWatch, lineDispatch } = media;
describe('lintVoice — the banned list is law', () => {
test.each([
['This one is a lock!', 2], // "lock" + exclamation
['🔥 free money, whos tailing', 3],
['Were so back 🚀', 2],
])('%s → %i violations', (text, n) => {
expect(lintVoice(text).length).toBeGreaterThanOrEqual(n);
});
test('clean wire copy passes', () => {
expect(lintVoice('THE SETTLE — Jul 11\nA-tier: 6-2\nvyndr.app/ledger')).toEqual([]);
});
});
describe('every template output passes its own lint', () => {
const NOW = '2026-07-12T14:05:00.000Z';
const GRADE = {
player: 'Aaron Judge', stat_type: 'total_bases', line: 1.5, direction: 'over',
grade: 'A', confidence: 78, archetype: 'BOMBER',
gradedAt: { line: 1.5, odds: -120, timestamp: NOW },
};
test('morning wire: real counts, stark from the library, no bans', () => {
const text = morningWire({ dateIso: NOW, counts: { mlb: 15, wnba: 3, nba: 0 }, loudest: GRADE });
expect(text).toContain('15 MLB.');
expect(text).toContain('3 WNBA.');
expect(text).not.toContain('0 NBA'); // zero sports never listed
expect(text).toContain('Aaron Judge');
expect(text).toContain('vyndr.app');
expect(lintVoice(text)).toEqual([]);
});
test('signal: player + line + grade + ET timestamp with real odds', () => {
const text = signal(GRADE);
expect(text).toContain('Aaron Judge — TB o1.5 · A');
expect(text).toContain('BOMBER read.');
expect(text).toContain('at -120');
expect(lintVoice(text)).toEqual([]);
});
test('the settle: counts always, percentages NEVER under n≥20', () => {
const agg = { settled: 8, hits: 5, misses: 3, pushes: 0, hit_pct: null, pending: 16, by_tier: { A: { settled: 4, hits: 3, misses: 1, hit_pct: null } } };
const misses = [{ player_name: 'Aja Wilson', stat: 'points', line: 23.5, side: 'over' }];
const text = theSettle({ aggregate: agg, misses, dateIso: NOW });
expect(text).toContain('All grades: 5-3');
expect(text).toContain('A-tier: 3-1');
expect(text).not.toContain('%'); // no percentage below the gate
expect(text).toContain('Record building — 8 settled');
expect(text).toContain('Wilson PTS o23.5 ❌'); // misses BY NAME
expect(lintVoice(text)).toEqual([]);
});
test('receipt: lock beside close beside result', () => {
const row = {
player_name: 'Aaron Judge', stat: 'total_bases', line: 1.5, side: 'over', grade: 'A',
graded_at: NOW, locked_odds: '-120', closing_line: 1.5, closing_odds: '-145',
clv: 0, clv_result: 'flat', outcome: 'hit', actual_value: 2, settled_at: NOW,
};
const text = receipt(row);
expect(text).toContain('Posted');
expect(text).toContain('Judge TB o1.5 · A · -120');
expect(text).toContain('Close: o1.5 at -145');
expect(text).toContain('Result: 2 ✅');
expect(lintVoice(text)).toEqual([]);
});
test('streak watch: lens reads only, absent lens rows dropped', () => {
const rows = [
{ player: 'Brice Turang', lens: { read: '12-game on-base streak; tonight @ Pittsburgh Pirates' } },
{ player: 'No Lens Guy', lens: { read: null } },
];
const text = streakWatch(rows, NOW);
expect(text).toContain('Turang');
expect(text).not.toContain('No Lens Guy');
expect(lintVoice(text)).toEqual([]);
});
test('archetype watch needs ≥2 of a kind, else null', () => {
expect(archetypeWatch([GRADE], NOW)).toBeNull();
const two = archetypeWatch([GRADE, { ...GRADE, player: 'Shohei Ohtani' }], NOW);
expect(two).toContain('BOMBER watch: 2');
expect(lintVoice(two)).toEqual([]);
});
test('line dispatch from a real movement row', () => {
const text = lineDispatch({ ...GRADE, movement: { kind: 'steam', delta: 1, currentLine: 2.5 } }, NOW);
expect(text).toContain('o1.5 → o2.5 ▲');
expect(text).toContain('Steam.');
expect(lintVoice(text)).toEqual([]);
});
});
describe('stark layer — selected, never generated; deterministic', () => {
test('same date → same line; comes from the committed library', () => {
const a = starkLine('morning', '2026-07-12T10:00:00Z');
const b = starkLine('morning', '2026-07-12T22:00:00Z');
expect(a).toBe(b);
expect(media.__internals.STARK.morning).toContain(a);
});
test('the whole library passes lint', () => {
for (const lines of Object.values(media.__internals.STARK)) {
if (!Array.isArray(lines)) continue;
for (const l of lines) expect(lintVoice(l)).toEqual([]);
}
});
});
describe('segmentThread — ≤280 per tweet on line boundaries', () => {
test('splits long threads and preserves every line', () => {
const long = Array.from({ length: 30 }, (_, i) => `Line ${i} of the settle with some padding text`).join('\n');
const tweets = segmentThread(long);
expect(tweets.length).toBeGreaterThan(1);
for (const t of tweets) expect(t.length).toBeLessThanOrEqual(280);
expect(tweets.join('\n')).toBe(long);
});
});
describe('deskService — pack assembly from pipeline data only', () => {
const { assembleDeskPack } = require('../../src/services/deskService');
test('assembles formats + data brief from injected caches', async () => {
const NOW = '2026-07-12T14:05:00.000Z';
const store = {
'snapshot:mlb:latest': { grades: [
{ player: 'Aaron Judge', stat_type: 'total_bases', line: 1.5, direction: 'over', grade: 'A', confidence: 80, archetype: 'BOMBER', gradedAt: { line: 1.5, odds: -120, timestamp: NOW } },
{ player: 'Shohei Ohtani', stat_type: 'home_runs', line: 0.5, direction: 'over', grade: 'A', confidence: 76, archetype: 'BOMBER', gradedAt: { line: 0.5, odds: 130, timestamp: NOW } },
] },
};
const pack = await assembleDeskPack({
cacheGet: async (k) => store[k] ?? null,
loadRosterLogs: async () => [],
ledger: { getModelAggregate: async () => ({ settled: 0, hits: 0, misses: 0, pushes: 0, hit_pct: null, pending: 24 }), __internals: { isConfigured: () => false } },
fetchSettled: async () => [],
now: () => NOW,
});
expect(pack.formats.morning_wire[0].text).toContain('2 MLB.');
expect(pack.formats.signals.length).toBe(2);
expect(pack.formats.archetype_watch[0].text).toContain('BOMBER watch: 2');
// variants: wire + numbers-only when a stark line was present
expect(pack.formats.morning_wire.length).toBeGreaterThanOrEqual(1);
expect(pack.data_brief.top_signals[0].player).toBe('Aaron Judge');
expect(pack.data_brief.record.pending).toBe(24);
// every emitted text passes lint
const all = JSON.stringify(pack.formats);
expect(all).not.toContain('!');
});
});
describe('ghostPublisher — drafts only, env-gated, no deps', () => {
const ghost = require('../../src/services/ghostPublisher');
test('JWT is a valid HS256 three-parter with the key id', () => {
const t = ghost.ghostJwt('abc123:6465616462656566', 1_752_000_000);
const [h, p] = t.split('.');
const header = JSON.parse(Buffer.from(h, 'base64url').toString());
const payload = JSON.parse(Buffer.from(p, 'base64url').toString());
expect(header.kid).toBe('abc123');
expect(payload.aud).toBe('/admin/');
expect(payload.exp - payload.iat).toBe(300);
});
test('unconfigured → no-op, never a throw', async () => {
const r = await ghost.saveDraft({ title: 't', html: '<p>x</p>' });
expect(r.ok).toBe(false);
expect(r.reason).toBe('ghost not configured');
});
test('posts DRAFT status only', async () => {
let body = null;
const r = await ghost.saveDraft({ title: 't', html: '<p>x</p>' }, {
force: true, url: 'https://blog.test', adminKey: 'id:6162',
fetchImpl: async (url, opts) => { body = JSON.parse(opts.body); return { ok: true, json: async () => ({ posts: [{ id: 'p1' }] }) }; },
});
expect(r.ok).toBe(true);
expect(body.posts[0].status).toBe('draft'); // NOTHING auto-publishes
});
});