From 629ff7ff58f8fd0bc7b39bb1c3c83485ab8b619b Mon Sep 17 00:00:00 2001 From: Kjell Tore Guttormsen Date: Thu, 18 Jun 2026 11:48:24 +0200 Subject: [PATCH] =?UTF-8?q?refactor(linkedin-studio):=20M0-9=20=E2=80=94?= =?UTF-8?q?=20REMEMBER.md=20materializes=20external=20(anti-pattern=20fix)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__tests__/session-start-remember.test.mjs | 36 +++++++++++++++++++ hooks/scripts/session-start.mjs | 4 ++- 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 hooks/scripts/__tests__/session-start-remember.test.mjs diff --git a/hooks/scripts/__tests__/session-start-remember.test.mjs b/hooks/scripts/__tests__/session-start-remember.test.mjs new file mode 100644 index 0000000..3f65140 --- /dev/null +++ b/hooks/scripts/__tests__/session-start-remember.test.mjs @@ -0,0 +1,36 @@ +import { describe, test } from 'node:test'; +import assert from 'node:assert/strict'; +import { execFileSync } from 'node:child_process'; +import { mkdtempSync, rmSync, existsSync } from 'node:fs'; +import { join, dirname } from 'node:path'; +import { tmpdir } from 'node:os'; +import { fileURLToPath } from 'node:url'; + +// M0-9: REMEMBER.md must materialize under the EXTERNAL data root (getDataRoot()), +// not inside the plugin tree (the old anti-pattern wrote join(PLUGIN_ROOT, +// 'REMEMBER.md')). session-start is a procedural hook with no exports, so we run +// it as a subprocess with an isolated HOME + LINKEDIN_STUDIO_DATA and check where +// REMEMBER.md lands. Pattern: user-prompt-context.test.mjs subprocess harness. +const hookPath = join(dirname(fileURLToPath(import.meta.url)), '..', 'session-start.mjs'); + +describe('session-start — REMEMBER.md materializes external (M0-9)', () => { + test('REMEMBER.md is created under the external data root, not the plugin tree', () => { + const home = mkdtempSync(join(tmpdir(), 'lis-ss-home-')); + const dataDir = mkdtempSync(join(tmpdir(), 'lis-ss-data-')); + try { + execFileSync('node', [hookPath], { + input: '', + env: { ...process.env, HOME: home, USERPROFILE: home, LINKEDIN_STUDIO_DATA: dataDir }, + encoding: 'utf-8', + }); + + assert.ok( + existsSync(join(dataDir, 'REMEMBER.md')), + 'REMEMBER.md must be created under the external data root', + ); + } finally { + rmSync(home, { recursive: true, force: true }); + rmSync(dataDir, { recursive: true, force: true }); + } + }); +}); diff --git a/hooks/scripts/session-start.mjs b/hooks/scripts/session-start.mjs index 4c067b7..86fd23d 100644 --- a/hooks/scripts/session-start.mjs +++ b/hooks/scripts/session-start.mjs @@ -8,6 +8,7 @@ import { fileURLToPath } from 'node:url'; import { calculateScore } from './personalization-score.mjs'; import { queueToday, queueOverdue, queueUpcoming } from './queue-manager.mjs'; import { applyWeekRollover } from './week-rollover.mjs'; +import { getDataRoot } from './data-root.mjs'; const __dirname = dirname(fileURLToPath(import.meta.url)); const PLUGIN_ROOT = join(__dirname, '..', '..'); @@ -405,10 +406,11 @@ if (existsSync(STATE_FILE)) { } // Read REMEMBER.md for user session context -const rememberFile = join(PLUGIN_ROOT, 'REMEMBER.md'); +const rememberFile = join(getDataRoot(), 'REMEMBER.md'); const rememberTemplate = join(PLUGIN_ROOT, 'config', 'REMEMBER.template.md'); if (!existsSync(rememberFile) && existsSync(rememberTemplate)) { + mkdirSync(dirname(rememberFile), { recursive: true }); copyFileSync(rememberTemplate, rememberFile); let rememberContent = readFileSync(rememberFile, 'utf-8'); const today = new Date().toISOString().slice(0, 10);