feat(linkedin-studio): SB-S2 consolidation engine — proposeDiff/applyDiff [skip-docs]
Pure deterministic engine over the two-layer profile: add / reject-ai-draft / evidence-bump / promote-at-N(3) / conflict-keep-both / decay-flag(90d). No-duplicate-id guarantee: primary id = mintEntityId(observed,key), conflict-alt id = mintContentId(observed-alt🔑:value::date) — byte-distinct. Folded profile-field seeds immutable (different kind); static facts decay-exempt; no supersede (S3). applyDiff produces the next ProfileDoc that round-trips through parse/serialize; re-running is idempotent (bump, not duplicate). + consolidation-state.json sidecar IO. 12 engine tests (SC1a–g, SC2, SC3, SC4). brain 63→75, tsc clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RigJBiRFNtFZKCz21qNbQ4
This commit is contained in:
parent
204665e90b
commit
ff39d14206
2 changed files with 346 additions and 0 deletions
160
scripts/brain/tests/consolidate.test.ts
Normal file
160
scripts/brain/tests/consolidate.test.ts
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
import { describe, test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import { proposeDiff, applyDiff, type Candidate } from "../src/consolidate.js";
|
||||
import { mintEntityId } from "../src/id.js";
|
||||
import { parseProfile, serializeProfile } from "../src/profile.js";
|
||||
import type { ProfileDoc, ProfileFact } from "../src/types.js";
|
||||
|
||||
const TODAY = "2026-06-23";
|
||||
const observedId = (key: string) => mintEntityId({ kind: "observed", key });
|
||||
|
||||
const fact = (over: Partial<ProfileFact>): ProfileFact => ({
|
||||
id: "000000000000",
|
||||
value: "v",
|
||||
first_seen: "2026-01-01",
|
||||
last_seen: TODAY,
|
||||
evidence_count: 1,
|
||||
provenance: "published",
|
||||
status: "active",
|
||||
...over,
|
||||
});
|
||||
const doc = (staticF: ProfileFact[], dynamicF: ProfileFact[]): ProfileDoc => ({
|
||||
schemaVersion: 1,
|
||||
static: staticF,
|
||||
dynamic: dynamicF,
|
||||
});
|
||||
const cand = (over: Partial<Candidate>): Candidate => ({
|
||||
key: "k",
|
||||
value: "v",
|
||||
provenance: "published",
|
||||
source: "manual",
|
||||
observed_date: TODAY,
|
||||
...over,
|
||||
});
|
||||
|
||||
describe("proposeDiff classification (SC1a–g)", () => {
|
||||
test("SC1a — add: a published candidate with no match becomes a new dynamic fact", () => {
|
||||
const diff = proposeDiff({ current: doc([], []), candidates: [cand({ key: "topic", value: "AI safety" })], today: TODAY });
|
||||
assert.equal(diff.additions.length, 1);
|
||||
assert.equal(diff.additions[0].value, "AI safety");
|
||||
assert.equal(diff.additions[0].id, observedId("topic"));
|
||||
assert.equal(diff.additions[0].evidence_count, 1);
|
||||
assert.equal(diff.additions[0].status, "active");
|
||||
});
|
||||
|
||||
test("SC1b — reject: an ai-draft candidate produces an empty diff", () => {
|
||||
const diff = proposeDiff({ current: doc([], []), candidates: [cand({ provenance: "ai-draft" })], today: TODAY });
|
||||
assert.deepEqual(diff.additions, []);
|
||||
assert.deepEqual(diff.evidenceBumps, []);
|
||||
assert.deepEqual(diff.promotions, []);
|
||||
assert.deepEqual(diff.conflicts, []);
|
||||
});
|
||||
|
||||
test("SC1c — evidence-bump: same key+value bumps the existing fact, no addition", () => {
|
||||
const id = observedId("topic");
|
||||
const cur = doc([], [fact({ id, value: "AI safety", evidence_count: 1 })]);
|
||||
const diff = proposeDiff({ current: cur, candidates: [cand({ key: "topic", value: "AI safety" })], today: TODAY });
|
||||
assert.equal(diff.additions.length, 0);
|
||||
assert.equal(diff.evidenceBumps.length, 1);
|
||||
assert.deepEqual(diff.evidenceBumps[0], { id, newCount: 2, last_seen: TODAY });
|
||||
});
|
||||
|
||||
test("SC1d — promote: a dynamic fact reaching N=3 is promoted", () => {
|
||||
const id = observedId("topic");
|
||||
const cur = doc([], [fact({ id, value: "AI safety", evidence_count: 2 })]);
|
||||
const diff = proposeDiff({ current: cur, candidates: [cand({ key: "topic", value: "AI safety" })], today: TODAY });
|
||||
assert.equal(diff.evidenceBumps[0].newCount, 3);
|
||||
assert.deepEqual(diff.promotions, [{ id }]);
|
||||
});
|
||||
|
||||
test("SC1d — no promote below N", () => {
|
||||
const id = observedId("topic");
|
||||
const cur = doc([], [fact({ id, value: "x", evidence_count: 1 })]);
|
||||
const diff = proposeDiff({ current: cur, candidates: [cand({ key: "topic", value: "x" })], today: TODAY });
|
||||
assert.deepEqual(diff.promotions, []);
|
||||
});
|
||||
|
||||
test("SC1e — conflict: different value keeps BOTH with DISTINCT ids, old fact untouched", () => {
|
||||
const primaryId = observedId("role");
|
||||
const cur = doc([], [fact({ id: primaryId, value: "advisor", evidence_count: 2 })]);
|
||||
const diff = proposeDiff({ current: cur, candidates: [cand({ key: "role", value: "architect" })], today: TODAY });
|
||||
assert.equal(diff.conflicts.length, 1);
|
||||
assert.equal(diff.conflicts[0].primaryId, primaryId);
|
||||
assert.equal(diff.conflicts[0].primaryValue, "advisor");
|
||||
assert.notEqual(diff.conflicts[0].altId, primaryId, "alt id must differ from primary id (no duplicate-id corruption)");
|
||||
// the alt fact is added; the old fact is NOT bumped
|
||||
assert.equal(diff.additions.length, 1);
|
||||
assert.equal(diff.additions[0].value, "architect");
|
||||
assert.equal(diff.additions[0].id, diff.conflicts[0].altId);
|
||||
assert.deepEqual(diff.evidenceBumps, []);
|
||||
});
|
||||
|
||||
test("SC1f — decay: a dynamic fact older than 90d is flagged; a static fact is decay-exempt", () => {
|
||||
const old = "2026-03-01"; // ~114 days before TODAY
|
||||
const dyn = fact({ id: observedId("stale-topic"), value: "old", last_seen: old });
|
||||
const stat = fact({ id: observedId("settled"), value: "stable", last_seen: old });
|
||||
const cur = doc([stat], [dyn]);
|
||||
const diff = proposeDiff({ current: cur, candidates: [], today: TODAY });
|
||||
assert.equal(diff.staleFlags.length, 1, "only the dynamic stale fact is flagged");
|
||||
assert.equal(diff.staleFlags[0].id, dyn.id);
|
||||
assert.ok(diff.staleFlags[0].daysStale > 90);
|
||||
});
|
||||
|
||||
test("SC1f — a fresh dynamic fact is not flagged", () => {
|
||||
const cur = doc([], [fact({ id: observedId("fresh"), last_seen: "2026-06-20" })]);
|
||||
const diff = proposeDiff({ current: cur, candidates: [], today: TODAY });
|
||||
assert.deepEqual(diff.staleFlags, []);
|
||||
});
|
||||
|
||||
test("SC1g — folded immutable: a candidate overlapping a profile-field seed adds a new observed fact, seed untouched", () => {
|
||||
const foldedId = mintEntityId({ kind: "profile-field", key: "role" });
|
||||
const folded = fact({ id: foldedId, value: "advisor", provenance: "human" });
|
||||
const cur = doc([folded], []);
|
||||
const before = serializeProfile(cur);
|
||||
const diff = proposeDiff({ current: cur, candidates: [cand({ key: "role", value: "architect" })], today: TODAY });
|
||||
// candidate keys to observed:role (≠ profile-field:role) → no match → ADD, no conflict on the folded seed
|
||||
assert.equal(diff.additions.length, 1);
|
||||
assert.equal(diff.additions[0].id, observedId("role"));
|
||||
assert.equal(diff.conflicts.length, 0);
|
||||
assert.equal(serializeProfile(cur), before, "input doc not mutated; folded seed untouched");
|
||||
});
|
||||
});
|
||||
|
||||
describe("proposeDiff purity + applyDiff round-trip (SC2, SC3, SC4)", () => {
|
||||
test("SC2 — proposeDiff does not mutate its inputs", () => {
|
||||
const cur = doc([], [fact({ id: observedId("topic"), value: "x" })]);
|
||||
const candidates = [cand({ key: "topic", value: "x" }), cand({ key: "new", value: "y" })];
|
||||
const snapCur = JSON.stringify(cur);
|
||||
const snapCand = JSON.stringify(candidates);
|
||||
proposeDiff({ current: cur, candidates, today: TODAY });
|
||||
assert.equal(JSON.stringify(cur), snapCur, "current unchanged");
|
||||
assert.equal(JSON.stringify(candidates), snapCand, "candidates unchanged");
|
||||
});
|
||||
|
||||
test("SC3 — applyDiff(current, proposeDiff(...)) round-trips through parse/serialize", () => {
|
||||
const cur = doc([fact({ id: mintEntityId({ kind: "profile-field", key: "name" }), value: "KTG", provenance: "human" })], [fact({ id: observedId("topic"), value: "AI safety", evidence_count: 2 })]);
|
||||
const candidates = [
|
||||
cand({ key: "topic", value: "AI safety" }), // bump→promote
|
||||
cand({ key: "new-topic", value: "governance" }), // add
|
||||
cand({ key: "topic", value: "alignment" }), // conflict on topic → alt
|
||||
];
|
||||
const diff = proposeDiff({ current: cur, candidates, today: TODAY });
|
||||
const next = applyDiff(cur, diff);
|
||||
const round = parseProfile(serializeProfile(next));
|
||||
assert.deepEqual(round, next, "applied doc round-trips exactly");
|
||||
// every fact id is unique (no duplicate-id corruption)
|
||||
const ids = [...round.static, ...round.dynamic].map((f) => f.id);
|
||||
assert.equal(new Set(ids).size, ids.length, "all fact ids unique");
|
||||
});
|
||||
|
||||
test("SC4 — idempotent: re-running propose→apply with the same candidates adds no duplicate facts", () => {
|
||||
const cur = doc([], []);
|
||||
const candidates = [cand({ key: "topic", value: "AI safety" })];
|
||||
const once = applyDiff(cur, proposeDiff({ current: cur, candidates, today: TODAY }));
|
||||
const twice = applyDiff(once, proposeDiff({ current: once, candidates, today: TODAY }));
|
||||
const factsOnce = [...once.static, ...once.dynamic].length;
|
||||
const factsTwice = [...twice.static, ...twice.dynamic].length;
|
||||
assert.equal(factsTwice, factsOnce, "no duplicate fact on re-run (bump only)");
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue