linkedin-studio/scripts/brain/tests/assemble.test.ts
Kjell Tore Guttormsen edd3e15ef7 feat(linkedin-studio): SB-S3c — cross-silo id-threading + post→analytics assembler [skip-docs]
Hub-side design: the published record now carries the specifics/trends ids
it was built from (additive, omit-empty → byte-backward-compatible), and a
new pure assembler (scripts/brain/src/assemble.ts + `brain assemble`) joins
post↔analytics by normalized title-prefix + date with honest confidence
tiers (high/low/none). Answers the arc's north-star query: which raw
material actually performs? (specific → post → measured analytics).

All four tributaries untouched (analytics READ-only via inlined raw-JSON,
no package import); profile.md grammar untouched (the fact→post link stays
OUT — C-1). The repeatable --specific/--trend ingest flags collect via a
new collectRepeated helper, leaving parseFlags untouched.

TDD: 19 new brain tests (ingest 4 + publish 3 + assemble 8 + cli 4), all
SC1–SC12. brain 113/113, gate 95/0/0, BRAIN_TESTS_FLOOR 94→113,
ASSERT_BASELINE_FLOOR unchanged at 80. Light-Voyage hardened
(brief-review 5 FIX · plan-critic 1 BLOCK+4 MAJOR+4 MINOR · scope-guardian ALIGNED).

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

95 lines
3.9 KiB
TypeScript

import { describe, test } from "node:test";
import assert from "node:assert/strict";
import { mintContentId } from "../src/id.js";
import type { PublishedRecord } from "../src/ingest.js";
import { assemblePostGraph, type AnalyticsRowInput } from "../src/assemble.js";
const DATE = "2026-05-26";
function rec(body: string, over: Partial<PublishedRecord> = {}): PublishedRecord {
return {
id: mintContentId(body),
provenance: "published",
published_date: DATE,
captured_at: "2026-06-23",
source: "manual",
specifics: [],
trends: [],
body,
...over,
};
}
function row(title: string, over: Partial<AnalyticsRowInput> = {}): AnalyticsRowInput {
return { title, publishedDate: DATE, metrics: { engagementRate: 4.2 }, ...over };
}
// A body whose normalized opener is ≥ PREFIX_FLOOR (24) chars.
const BODY = "Jeg lærte noe viktig om dømmekraft i dag.\n\nDel 1 av serien.";
const FULL_TITLE = "Jeg lærte noe viktig om dømmekraft i dag.";
describe("SB-S3c assembler — assemblePostGraph (SC6/SC7/SC8)", () => {
// SC6 — high confidence: body begins with the row title (≥floor) + same date.
test("SC6: prefix-match + same date → high, whole row attached, specifics/trends surfaced", () => {
const r = rec(BODY, { specifics: ["aaaaaaaaaaaa"], trends: ["bbbbbbbbbbbb"] });
const graph = assemblePostGraph({ records: [r], analytics: [row(FULL_TITLE)] });
assert.equal(graph.length, 1);
assert.equal(graph[0].contentId, r.id);
assert.equal(graph[0].match.confidence, "high");
assert.deepEqual(graph[0].match.row, row(FULL_TITLE)); // the whole row reference (FIX 4)
assert.deepEqual(graph[0].specifics, ["aaaaaaaaaaaa"]);
assert.deepEqual(graph[0].trends, ["bbbbbbbbbbbb"]);
});
// SC7 — low / none / below-floor / ellipsis near-miss.
test("SC7a: no prefix match → none", () => {
const graph = assemblePostGraph({
records: [rec(BODY)],
analytics: [row("Completely unrelated opening sentence here")],
});
assert.equal(graph[0].match.confidence, "none");
assert.equal(graph[0].match.row, undefined);
});
test("SC7b: a too-short (< floor) title that is a literal prefix → none (floor guards false high)", () => {
const graph = assemblePostGraph({ records: [rec(BODY)], analytics: [row("Jeg lærte")] });
assert.equal(graph[0].match.confidence, "none");
});
test("SC7c: a LinkedIn-truncated '…' title + same date → high (ellipsis stripped)", () => {
const graph = assemblePostGraph({
records: [rec(BODY)],
analytics: [row("Jeg lærte noe viktig om dømmekraft i…")],
});
assert.equal(graph[0].match.confidence, "high");
});
test("SC7d: prefix match but a different date → low (surfaced, not hidden)", () => {
const graph = assemblePostGraph({
records: [rec(BODY)],
analytics: [row(FULL_TITLE, { publishedDate: "2026-05-20" })],
});
assert.equal(graph[0].match.confidence, "low");
assert.ok(graph[0].match.row, "row still attached on a low match");
});
// SC8 — pure + total.
test("SC8: empty records → empty graph", () => {
assert.deepEqual(assemblePostGraph({ records: [], analytics: [row(FULL_TITLE)] }), []);
});
test("SC8: empty analytics → every post present with match none, no throw", () => {
const graph = assemblePostGraph({ records: [rec(BODY), rec("another post body here")], analytics: [] });
assert.equal(graph.length, 2);
assert.ok(graph.every((g) => g.match.confidence === "none"));
});
test("SC8: best-of prefers high over low and is deterministic", () => {
const r = rec(BODY);
const high = row(FULL_TITLE); // same date
const low = row(FULL_TITLE, { publishedDate: "2026-01-01" }); // different date
const graph = assemblePostGraph({ records: [r], analytics: [low, high] });
assert.equal(graph[0].match.confidence, "high", "high beats low regardless of input order");
});
});