feat(linkedin-studio): RE-R2b — dated morning-brief artifact + session-start surfacing [skip-docs]

The visible layer of R2. Pure brief.ts: rankForBrief (pillar-overlap -> recency over
the store; publishedAt ?? capturedAt freshness, 7d window; total-order sort), renderBrief
(dated Markdown + hook-surfaceable summary frontmatter), briefSummary (one summary source),
defaultBriefDir (derived from defaultStorePath). CLI `brief` writes
<data>/trends/morning-brief/YYYY-MM-DD.md; session-start surfaces the latest zero-tsx
(latestMorningBrief). Wired into trend-spotter Step 4.6 (scan->capture->brief->surfaced).
No store-schema/scoring change; no scheduler (R3).

25 new trends tests (21 brief.test + 4 cli brief, RED-first) + 3 hook tests (morning-brief
surfacing). trends 104/104 (floor 104), hook-suite 139/139, gate FAIL=0 (ASSERT floor 94,
Section 16i: cli brief-handler + trend-spotter brief-pointer + session-start surfacing
greps), tsc clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VmHCQjJHUyWwxGAVVjNLgp
This commit is contained in:
Kjell Tore Guttormsen 2026-06-24 13:12:54 +02:00
commit fa7551070e
9 changed files with 712 additions and 10 deletions

View file

@ -51,6 +51,31 @@ function trendsNewestCapture(storePath) {
}
}
// RE-R2b: the most recent morning brief (date + pre-rendered summary) for
// session-start surfacing. Reads the dated Markdown directly — NO tsx (same
// zero-dep discipline as trendsNewestCapture above): the brief's frontmatter
// carries a single-line `summary` the hook surfaces verbatim (extractYaml's
// [^"\n]* + .trim() guarantees date/summary are newline-free). Returns null when
// the dir is absent / empty / unreadable — no brief yet => no block.
function latestMorningBrief(briefDir) {
if (!existsSync(briefDir)) return null;
try {
const files = readdirSync(briefDir)
.filter((f) => /^\d{4}-\d{2}-\d{2}\.md$/.test(f))
.sort();
const name = files[files.length - 1];
if (!name) return null;
const content = readFileSync(join(briefDir, name), 'utf-8');
return {
date: extractYaml(content, 'date'),
summary: extractYaml(content, 'summary'),
file: join(briefDir, name),
};
} catch {
return null;
}
}
// SB-S2: brain consolidation freshness. Both read zero-dep through getDataRoot —
// the SAME data root the brain CLI's --apply writes to (sidecar reachable by both).
// No tsx, no profile.md parse: a readdir count + a tiny JSON read, cost-bounded.
@ -503,6 +528,14 @@ if (brainProfileMissing) {
context += '\\n## Brain\\n- Brain not initialised. Run `brain init` to seed your evolving profile.\\n';
}
// RE-R2b: surface the latest dated morning brief (zero-tsx; summary is a
// pre-rendered single line from the brief's frontmatter). Unconditional on a brief
// existing, like the brain nudge above (so the fresh-install branch surfaces it too).
const latestBrief = latestMorningBrief(join(getDataRoot('trends'), 'morning-brief'));
if (latestBrief && latestBrief.summary) {
context += `\\n## Morning Brief (${latestBrief.date})\\n${latestBrief.summary}\\n→ Full brief: ${latestBrief.file}\\n`;
}
// Read REMEMBER.md for user session context
const rememberFile = join(getDataRoot(), 'REMEMBER.md');
const rememberTemplate = join(PLUGIN_ROOT, 'config', 'REMEMBER.template.md');