linkedin-studio/hooks/scripts/__tests__/session-start-brain-consolidation.test.mjs
Kjell Tore Guttormsen 2d01dfca52 feat(linkedin-studio): SB-S2 session-start scaffold-ensure + consolidation nudge [skip-docs]
session-start.mjs (zero-dep, Edit): unconditional brain/+ingest/ scaffold-ensure
(mkdir, runs on the fresh-install path too); a consolidation-due nudge in the
reminders block (counts published records + reads consolidation-state.json last_run
via getDataRoot — same root the CLI --apply writes; no profile.md parse, \n idiom,
null-safe never-nags); a brain-init nudge appended AFTER the if/else so the
fresh-install branch's context reassignment can't clobber it. + readdirSync import.
SC6 hook test (5 cases) via heredoc. Hook suite 131→136/0; no recompile (body-only edit).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RigJBiRFNtFZKCz21qNbQ4
2026-06-23 17:07:48 +02:00

80 lines
3.7 KiB
JavaScript

import { describe, test } from 'node:test';
import assert from 'node:assert/strict';
import { execFileSync } from 'node:child_process';
import { mkdtempSync, rmSync, mkdirSync, writeFileSync, copyFileSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { tmpdir } from 'node:os';
import { fileURLToPath } from 'node:url';
// SB-S2: SessionStart emits a "run brain consolidate" nudge driven by the count of
// published gold records + the consolidation-state.json last_run sidecar (read via
// getDataRoot — the SAME root the brain CLI --apply writes to). Zero-dep, no
// profile.md parse. Subprocess harness with isolated HOME + LINKEDIN_STUDIO_DATA,
// mirroring session-start-trends-staleness.test.mjs.
const here = dirname(fileURLToPath(import.meta.url));
const hookPath = join(here, '..', 'session-start.mjs');
const PLUGIN_ROOT = join(here, '..', '..', '..');
const STATE_TEMPLATE = join(PLUGIN_ROOT, 'config', 'state-file.template.md');
const NUDGE = 'brain consolidate';
const isoDaysAgo = (n) => new Date(Date.now() - n * 86400000).toISOString().slice(0, 10);
const record = (id) =>
`id: ${id}\nprovenance: published\npublished_date: 2026-01-01\ncaptured_at: 2026-01-01\nsource: manual\n---\nbody`;
function runHook({ withState = true, published = [], lastRun = undefined } = {}) {
const home = mkdtempSync(join(tmpdir(), 'lis-bc-home-'));
const data = mkdtempSync(join(tmpdir(), 'lis-bc-data-'));
try {
if (withState) {
mkdirSync(join(home, '.claude'), { recursive: true });
copyFileSync(STATE_TEMPLATE, join(home, '.claude', 'linkedin-studio.local.md'));
}
if (published.length) {
mkdirSync(join(data, 'ingest', 'published'), { recursive: true });
for (const id of published) writeFileSync(join(data, 'ingest', 'published', id + '.md'), record(id));
}
if (lastRun !== undefined) {
mkdirSync(join(data, 'brain'), { recursive: true });
writeFileSync(join(data, 'brain', 'consolidation-state.json'), JSON.stringify({ last_run: lastRun }));
}
const stdout = execFileSync('node', [hookPath], {
input: '',
env: { ...process.env, HOME: home, USERPROFILE: home, LINKEDIN_STUDIO_DATA: data },
encoding: 'utf-8',
});
return JSON.parse(stdout).hookSpecificOutput.additionalContext;
} finally {
rmSync(home, { recursive: true, force: true });
rmSync(data, { recursive: true, force: true });
}
}
describe('session-start — brain consolidation-due nudge (SB-S2)', () => {
test('fires when published records exist and consolidation never ran', () => {
const ctx = runHook({ published: ['aaaaaaaaaaaa', 'bbbbbbbbbbbb'] });
assert.ok(ctx.includes(NUDGE), 'nudge expected when published exist + never consolidated');
assert.ok(ctx.includes('2 published'), 'reports the published count');
});
test('fires when last_run is stale (>=7d)', () => {
const ctx = runHook({ published: ['aaaaaaaaaaaa'], lastRun: isoDaysAgo(10) });
assert.ok(ctx.includes(NUDGE), 'nudge expected for a 10-day-old last_run');
});
test('silent when last_run is fresh (<7d)', () => {
const ctx = runHook({ published: ['aaaaaaaaaaaa'], lastRun: isoDaysAgo(2) });
assert.ok(!ctx.includes(NUDGE), 'fresh consolidation => no nudge');
});
test('silent when there are no published records (never nags)', () => {
const ctx = runHook({ published: [] });
assert.ok(!ctx.includes(NUDGE), 'no published => no nudge');
});
test('fresh install (no state file) emits valid JSON and nudges brain init', () => {
const ctx = runHook({ withState: false });
assert.equal(typeof ctx, 'string', 'valid JSON additionalContext on fresh install');
assert.ok(ctx.includes('Brain not initialised'), 'brain-init nudge survives the fresh-install branch');
});
});