import test from "node:test"; import assert from "node:assert/strict"; import { createRequire } from "node:module"; import { readFileSync } from "node:fs"; import { fileURLToPath } from "node:url"; import { dirname, join } from "node:path"; const require = createRequire(import.meta.url); const SH = require("../shared.js"); const here = dirname(fileURLToPath(import.meta.url)); const fixture = JSON.parse(readFileSync(join(here, "fixtures", "jev-response.json"), "utf8")); function st(patch) { const s = SH.normalizeSettings({ enabled: true, apiKey: "test-key", combine: "and", rulesA: [{ enabled: true, word1: "Cats", word2: "dogs" }], rulesB: [{ enabled: false, word: "politics", above: true }], }); return Object.assign(s, patch || {}); } function cmp(i, probabilities) { return { ["compare" + i]: { type: "choice", probabilities } }; } function single(i, noul) { return { ["single" + i]: { type: "noul", noul } }; } test("normalizeSettings cleans words and applies defaults", () => { const d = SH.defaults(); assert.equal(d.enabled, false); assert.equal(d.combine, "and"); assert.deepEqual(d.rulesA, []); assert.deepEqual(d.rulesB, []); const s = SH.normalizeSettings({ rulesA: [{ word1: " Cats ", word2: "DOGS" }] }); assert.equal(s.rulesA[0].word1, "cats"); assert.equal(s.rulesA[0].word2, "dogs"); assert.equal(s.rulesA[0].enabled, false); const b = SH.normalizeSettings({ rulesB: [{ word: " Humor " }] }); assert.equal(b.rulesB[0].word, "humor"); assert.equal(b.rulesB[0].above, true); assert.equal(b.rulesB[0].enabled, false); }); test("legacy single-rule settings migrate to one-element clause arrays", () => { const s = SH.normalizeSettings({ enabled: true, combine: "or", ruleA: { enabled: true, word1: "Cats", word2: "dogs" }, ruleB: { enabled: true, word: "politics", above: false }, }); assert.deepEqual(s.rulesA, [{ enabled: true, word1: "cats", word2: "dogs", trained: { hide: [], keep: [] } }]); assert.deepEqual(s.rulesB, [{ enabled: true, word: "politics", above: false, trained: { hide: [], keep: [] } }]); assert.equal(s.combine, "or"); // and behave normally through the whole pipeline assert.equal(SH.isActive(s), true); assert.deepEqual(Object.keys(SH.buildQuestions(s)).sort(), ["compare0", "single0"]); }); test("clause A needs two distinct, non-reserved words", () => { const good = st(); assert.equal(SH.clauseAValid(good.rulesA[0]), true); const same = st(); same.rulesA[0].word2 = "cats"; assert.equal(SH.clauseAValid(same.rulesA[0]), false); const reserved = st(); reserved.rulesA[0].word2 = "neither"; assert.equal(SH.clauseAValid(reserved.rulesA[0]), false); const empty = st(); empty.rulesA[0].word1 = ""; assert.equal(SH.clauseAValid(empty.rulesA[0]), false); }); test("rule A boundary: show at exactly 50%, hide below", () => { const s = st(); assert.equal(SH.decideFromAnswers(s, cmp(0, { cats: 0.5, dogs: 0.4, neither: 0.1 })), "show"); assert.equal(SH.decideFromAnswers(s, cmp(0, { cats: 0.49, dogs: 0.41, neither: 0.1 })), "hide"); assert.equal(SH.decideFromAnswers(s, cmp(0, { cats: 0.51, dogs: 0.39, neither: 0.1 })), "show"); }); test("rule A with missing probability or malformed answers is null (fail open upstream)", () => { const s = st(); assert.equal(SH.decideFromAnswers(s, cmp(0, {})), null); assert.equal(SH.decideFromAnswers(s, null), null); assert.equal(SH.decideFromAnswers(s, {}), null); }); test("rule B is strict about 50% in both directions", () => { const s = st({ combine: "and" }); s.rulesA[0].enabled = false; s.rulesB[0].enabled = true; s.rulesB[0].above = true; assert.equal(SH.decideFromAnswers(s, single(0, 0.5)), "hide"); assert.equal(SH.decideFromAnswers(s, single(0, 0.51)), "show"); assert.equal(SH.decideFromAnswers(s, single(0, 0.49)), "hide"); s.rulesB[0].above = false; assert.equal(SH.decideFromAnswers(s, single(0, 0.5)), "hide"); assert.equal(SH.decideFromAnswers(s, single(0, 0.49)), "show"); assert.equal(SH.decideFromAnswers(s, single(0, 0.51)), "hide"); }); test("AND requires every enabled clause to pass; OR needs one", () => { const andS = st(); andS.rulesB[0].enabled = true; const passingA = cmp(0, { cats: 0.9, dogs: 0.05, neither: 0.05 }); const failingB = single(0, 0.2); // above=true → fail assert.equal(SH.decideFromAnswers(andS, Object.assign({}, passingA, failingB)), "hide"); andS.combine = "or"; assert.equal(SH.decideFromAnswers(andS, Object.assign({}, passingA, failingB)), "show"); }); test("multiple clauses of one type are all judged and combined", () => { const s = st(); s.rulesA.push({ enabled: true, word1: "birds", word2: "fish" }); const bothPass = Object.assign( cmp(0, { cats: 0.9, dogs: 0.05, neither: 0.05 }), cmp(1, { birds: 0.8, fish: 0.1, neither: 0.1 }) ); const secondFails = Object.assign( cmp(0, { cats: 0.9, dogs: 0.05, neither: 0.05 }), cmp(1, { birds: 0.2, fish: 0.7, neither: 0.1 }) ); assert.equal(SH.decideFromAnswers(s, bothPass), "show"); assert.equal(SH.decideFromAnswers(s, secondFails), "hide"); s.combine = "or"; assert.equal(SH.decideFromAnswers(s, secondFails), "show"); // a malformed second clause fails the whole verdict open (null), not just itself s.combine = "and"; assert.equal(SH.decideFromAnswers(s, Object.assign(cmp(0, { cats: 0.9 }), {})), null); }); test("disabled or invalid clauses are skipped entirely", () => { const s = st(); s.rulesA.push({ enabled: true, word1: "", word2: "" }); // invalid → ignored s.rulesA.push({ enabled: false, word1: "birds", word2: "fish" }); // off → ignored s.rulesB.push({ enabled: true, word: "humor", above: true }); // Only A0 (compare0) and B1 (single1) ask questions; an implementation that // consulted the skipped slots would find no answer and return null. const answers = Object.assign( cmp(0, { cats: 0.9, dogs: 0.05, neither: 0.05 }), single(1, 0.9) ); assert.equal(SH.decideFromAnswers(s, answers), "show"); assert.deepEqual(Object.keys(SH.buildQuestions(s)).sort(), ["compare0", "single1"]); }); test("isActive reflects master switch and clause validity", () => { assert.equal(SH.isActive(st()), true); const off = st({ enabled: false }); assert.equal(SH.isActive(off), false); const invalid = st(); invalid.rulesA[0].enabled = true; invalid.rulesA[0].word2 = ""; assert.equal(SH.isActive(invalid), false); // A invalid, B off invalid.rulesB[0].enabled = true; assert.equal(SH.isActive(invalid), true); // B carries it const blank = st({ rulesA: [], rulesB: [] }); assert.equal(SH.isActive(blank), false); }); test("cacheKey changes when rule B direction flips (above vs below)", () => { const s = st(); s.rulesA[0].enabled = false; s.rulesB[0].enabled = true; s.rulesB[0].above = true; const above = SH.cacheKey(s, "hello", ""); s.rulesB[0].above = false; const below = SH.cacheKey(s, "hello", ""); assert.notEqual(above, below); }); test("cacheKey changes when a clause is added, edited, disabled, or removed", () => { const s = st(); const k1 = SH.cacheKey(s, "hello", ""); const added = st(); added.rulesA.push({ enabled: true, word1: "birds", word2: "fish" }); assert.notEqual(k1, SH.cacheKey(added, "hello", "")); const edited = st(); edited.rulesA[0].word2 = "birds"; assert.notEqual(k1, SH.cacheKey(edited, "hello", "")); const disabled = st(); disabled.rulesA[0].enabled = false; assert.notEqual(k1, SH.cacheKey(disabled, "hello", "")); const removed = st({ rulesA: [] }); assert.notEqual(k1, SH.cacheKey(removed, "hello", "")); }); test("cacheKey is deterministic, config-sensitive, and id-independent", () => { const s = st(); const k1 = SH.cacheKey(s, "hello", ""); const k2 = SH.cacheKey(s, "hello", ""); assert.equal(k1, k2); assert.notEqual(SH.cacheKey(s, "hello", ""), SH.cacheKey(s, "hello2", "")); const or = st({ combine: "or" }); assert.notEqual(k1, SH.cacheKey(or, "hello", "")); // repost with identical text/alt shares a key (no id input at all) assert.equal(k1, SH.cacheKey(s, "hello", "")); }); test("buildQuestions includes only enabled valid clauses with skill-shaped payloads", () => { const s = st(); let q = SH.buildQuestions(s); assert.deepEqual(Object.keys(q).sort(), ["compare0"]); assert.equal(q.compare0.type, "choice"); assert.deepEqual(Object.keys(q.compare0.criteria).sort(), ["cats", "dogs", "neither"]); assert.match(q.compare0.instructions, /`post`/); const b = st(); b.rulesA[0].enabled = false; b.rulesB[0].enabled = true; q = SH.buildQuestions(b); assert.deepEqual(Object.keys(q), ["single0"]); assert.equal(q.single0.type, "noul"); assert.ok(q.single0.criteria.true && q.single0.criteria.false); }); test("buildQuestions keys clauses of the same type distinctly", () => { const s = st(); s.rulesA.push({ enabled: true, word1: "birds", word2: "fish" }); s.rulesB[0].enabled = true; s.rulesB.push({ enabled: true, word: "humor", above: false }); const q = SH.buildQuestions(s); assert.deepEqual(Object.keys(q).sort(), ["compare0", "compare1", "single0", "single1"]); assert.deepEqual(Object.keys(q.compare1.criteria).sort(), ["birds", "fish", "neither"]); assert.equal(q.single1.type, "noul"); // invalid/disabled clauses emit no question, so indexes stay aligned with the arrays const gappy = st(); gappy.rulesA.unshift({ enabled: true, word1: "", word2: "" }); // invalid, index 0 const q2 = SH.buildQuestions(gappy); assert.deepEqual(Object.keys(q2), ["compare1"]); }); test("buildRequest caps text and targets jev-latest", () => { const s = st(); const req = SH.buildRequest(s, { text: "a".repeat(SH.TEXT_CAP * 2), alt: "" }); assert.equal(req.model, "jev-latest"); assert.equal(req.state.post.text.length, SH.TEXT_CAP); assert.equal(req.questions.compare0.type, "choice"); }); test("isJudgeable treats blank text+alt as nothing to judge", () => { assert.equal(SH.isJudgeable({ text: " ", alt: "" }), false); assert.equal(SH.isJudgeable({ text: "", alt: "a cat" }), true); assert.equal(SH.isJudgeable({ text: "hi", alt: "" }), true); }); test("answerDetails extracts the scores code acts on", () => { const s = st(); assert.deepEqual(SH.answerDetails(s, fixture.answers), { p0: 0.72 }); const b = st(); b.rulesA[0].enabled = false; b.rulesB[0].enabled = true; assert.deepEqual(SH.answerDetails(b, fixture.answers), { n0: 0.81 }); assert.deepEqual(SH.answerDetails(b, null), {}); }); test("answerDetails reports each clause under its own key", () => { const s = st(); s.rulesA.push({ enabled: true, word1: "birds", word2: "fish" }); s.rulesB[0].enabled = true; const answers = Object.assign( cmp(0, { cats: 0.6, dogs: 0.3, neither: 0.1 }), cmp(1, { birds: 0.2, fish: 0.75, neither: 0.05 }), { single0: { type: "noul", noul: 0.123456 } } ); assert.deepEqual(SH.answerDetails(s, answers), { p0: 0.6, p1: 0.2, n0: 0.123 }); }); test("fixture response decides correctly for both rules", () => { const s = st(); // ruleA only assert.equal(SH.decideFromAnswers(s, fixture.answers), "show"); // cats 0.72 const b = st(); b.rulesA[0].enabled = false; b.rulesB[0].enabled = true; b.rulesB[0].above = true; assert.equal(SH.decideFromAnswers(b, fixture.answers), "show"); // noul 0.81 b.rulesB[0].above = false; assert.equal(SH.decideFromAnswers(b, fixture.answers), "hide"); }); /* ---------------- SUX-2: embedder interface ---------------- */ const goodEmbedder = () => ({ id: "test-model-q4@rev1", dims: 4, ready: async () => {}, embed: async (texts) => texts.map(() => new Float32Array([1, 0, 0, 0])), }); test("checkEmbedder accepts the documented shape and rejects broken ones", () => { assert.equal(SH.checkEmbedder(goodEmbedder()), null); assert.match(SH.checkEmbedder(null), /object/); assert.match(SH.checkEmbedder({}), /id/); assert.match(SH.checkEmbedder({ id: "x", dims: 0 }), /dims/); assert.match(SH.checkEmbedder({ id: "x", dims: 1.5 }), /dims/); assert.match(SH.checkEmbedder({ id: "x", dims: 4 }), /ready/); assert.match(SH.checkEmbedder({ id: "x", dims: 4, ready() {} }), /embed/); }); test("registerEmbedder stores and returns the runtime; bad ones throw", () => { const e = goodEmbedder(); SH.registerEmbedder(e); assert.equal(SH.getEmbedder(), e); assert.throws(() => SH.registerEmbedder({ id: "" }), /bad embedder/); assert.equal(SH.getEmbedder(), e); // unchanged after a failed registration }); test("cosine is 1 for identical vectors, 0 for orthogonal or zero vectors", () => { assert.equal(SH.cosine([1, 0], [1, 0]), 1); assert.equal(SH.cosine([1, 0], [0, 1]), 0); assert.equal(SH.cosine([0, 0], [1, 2]), 0); assert.ok(Math.abs(SH.cosine([1, 1], [1, 0]) - Math.SQRT1_2) < 1e-12); assert.equal(SH.cosine(null, [1]), 0); // missing embedding → 0, never NaN }); test("meanVector averages component-wise and refuses empty input", () => { assert.deepEqual(SH.meanVector([[1, 3], [3, 5]]), [2, 4]); assert.equal(SH.meanVector([]), null); }); /* ---------------- SUX-3: embedding cache + feed average ---------------- */ test("embCacheKey separates model id and content, unlike cacheKey it ignores rules", () => { const k1 = SH.embCacheKey("m1@r1", "hello", ""); assert.equal(k1, SH.embCacheKey("m1@r1", "hello", "")); assert.notEqual(k1, SH.embCacheKey("m1@r2", "hello", "")); // model rev invalidates assert.notEqual(k1, SH.embCacheKey("m1@r1", "hello2", "")); assert.notEqual(k1, SH.embCacheKey("m1@r1", "hello", "alt")); }); test("emaStep starts at the first vector and blends toward later ones", () => { const first = SH.emaStep(null, [1, 1], 0.5); assert.deepEqual(first, [1, 1]); const next = SH.emaStep(first, [3, 3], 0.5); assert.deepEqual(next, [2, 2]); // tiny alpha moves slowly const slow = SH.emaStep(first, [3, 3], 1 / 64); assert.ok(Math.abs(slow[0] - (1 + 2 / 64)) < 1e-12); }); test("quantEmb round-trips within int8 error and handles zero vectors", () => { const emb = [0.5, -0.25, 1, 0]; const back = SH.dequantEmb(SH.quantEmb(emb)); assert.equal(back.length, emb.length); for (let i = 0; i < emb.length; i++) { assert.ok(Math.abs(back[i] - emb[i]) <= 1 / 254, `dim ${i}: ${back[i]} vs ${emb[i]}`); } const zero = SH.dequantEmb(SH.quantEmb([0, 0, 0, 0])); assert.deepEqual(zero, [0, 0, 0, 0]); // cosine between original and requantized stays ~1 — the loss SUX-12 watches const big = Array.from({ length: 64 }, (_, i) => Math.sin(i)); assert.ok(SH.cosine(big, SH.dequantEmb(SH.quantEmb(big))) > 0.9999); }); /* ---------------- SUX-4: example rules ---------------- */ function stE(patch) { const s = SH.normalizeSettings({ enabled: true, combine: "and", rulesE: [{ enabled: true, name: "politics", examples: [{ text: "election news", alt: "" }, { text: "senate vote", alt: "" }], cutoff: 0.1, strictness: 0.5, }], }); return Object.assign(s, patch || {}); } test("normalizeSettings carries rule E with defaults", () => { const s = SH.normalizeSettings({ rulesE: [{ enabled: true, name: "Politics ", examples: [{ text: "a" }, { text: " " }] }], }); assert.equal(s.rulesE.length, 1); assert.equal(s.rulesE[0].name, "Politics"); assert.equal(s.rulesE[0].cutoff, 0); assert.equal(s.rulesE[0].strictness, 0.5); assert.equal(s.rulesE[0].examples.length, 1); // blank example dropped const d = SH.defaults(); assert.deepEqual(d.rulesE, []); }); test("clauseEValid needs at least one example; isActive counts rule E", () => { const s = stE(); assert.equal(SH.clauseEValid(s.rulesE[0]), true); assert.equal(SH.isActive(s), true); s.rulesE[0].examples = []; assert.equal(SH.clauseEValid(s.rulesE[0]), false); assert.equal(SH.isActive(s), false); }); test("scoreExampleRule: closer to examples than feed scores high", () => { const ex = [[1, 0], [0.9, 0.1]]; const feed = [[-1, 0]]; // post matches examples, opposite of feed assert.ok(SH.scoreExampleRule([1, 0], ex, [-1, 0]) > 0.9); // post matches feed, not examples assert.ok(SH.scoreExampleRule([-1, 0], ex, [-1, 0]) < -0.9); // no feed average yet → pure similarity to examples assert.ok(Math.abs(SH.scoreExampleRule([1, 0], ex, null) - SH.cosine([1, 0], [0.95, 0.05])) < 1e-9); }); test("decideFromScores hides at the E cutoff, shows below it", () => { const s = stE(); // cutoff 0.1 assert.equal(SH.decideFromScores(s, { e0: 0.2 }), "hide"); assert.equal(SH.decideFromScores(s, { e0: 0.1 }), "hide"); // at cutoff → hidden assert.equal(SH.decideFromScores(s, { e0: 0.09 }), "show"); assert.equal(SH.decideFromScores(s, { e0: null }), null); // unscored → fail open assert.equal(SH.decideFromScores(s, {}), null); }); test("E rule combines with word rules under AND/OR", () => { const s = stE({ combine: "and" }); s.rulesA = [{ enabled: true, word1: "cats", word2: "dogs" }]; // E hides (0.2 >= 0.1), A shows (0.9) → AND → hide assert.equal(SH.decideFromScores(s, { e0: 0.2, p0: 0.9 }), "hide"); s.combine = "or"; assert.equal(SH.decideFromScores(s, { e0: 0.2, p0: 0.9 }), "show"); assert.equal(SH.decideFromScores(s, { e0: 0.09, p0: 0.9 }), "show"); }); test("cacheKey for E reacts to examples, cutoff, strictness, and model id", () => { const s = stE(); const base = SH.cacheKey(s, "hello", "", "m1@r1"); assert.equal(base, SH.cacheKey(s, "hello", "", "m1@r1")); const otherModel = SH.cacheKey(s, "hello", "", "m1@r2"); assert.notEqual(base, otherModel); const s2 = stE(); s2.rulesE[0].examples.push({ text: "new example", alt: "" }); assert.notEqual(base, SH.cacheKey(s2, "hello", "", "m1@r1")); const s3 = stE(); s3.rulesE[0].cutoff = 0.2; assert.notEqual(base, SH.cacheKey(s3, "hello", "", "m1@r1")); const s4 = stE(); s4.rulesE[0].strictness = 0.8; assert.notEqual(base, SH.cacheKey(s4, "hello", "", "m1@r1")); // disabled E clause stops mattering const s5 = stE(); s5.rulesE[0].enabled = false; assert.notEqual(base, SH.cacheKey(s5, "hello", "", "m1@r1")); }); /* ---------------- SUX-5: automatic cutoff + strictness ---------------- */ test("zeroShotDescriptions are short, natural, and embedding-side only", () => { const d = SH.zeroShotDescriptions("angry"); assert.deepEqual(d, { pos: "A post about angry.", neg: "A post about something else." }); // contrast pair never repeats the word on the neg side (adjective-safe) assert.ok(!d.neg.includes("angry")); assert.equal(SH.zeroShotDescriptions(" politics ").pos, "A post about politics."); assert.equal(SH.zeroShotDescriptions("").pos, "A post about ."); }); test("zeroShotProb is a sigmoid over the cosine gap, 0.5 at zero", () => { assert.equal(SH.zeroShotProb(0.3, 0.3), 0.5); assert.ok(SH.zeroShotProb(0.31, 0.3) > 0.5); assert.ok(SH.zeroShotProb(0.29, 0.3) < 0.5); assert.ok(Math.abs(SH.zeroShotProb(0.5, 0.3) - 0.99995) < 1e-4); // 0.2 cosine (10x T) → ~certain assert.ok(Math.abs(SH.zeroShotProb(0.31, 0.3) - 0.6225) < 1e-3); // 0.01 cosine (half T) → ~62% }); /* ---------------- SUX-7: escalation ---------------- */ test("needsEscalation bands around the boundary", () => { assert.equal(SH.needsEscalation(0.5, 0.5, 0.1), true); assert.equal(SH.needsEscalation(0.59, 0.5, 0.1), true); assert.equal(SH.needsEscalation(0.6, 0.5, 0.1), false); // at the margin → local assert.equal(SH.needsEscalation(0.61, 0.5, 0.1), false); assert.equal(SH.needsEscalation(0.41, 0.5, 0.1), true); assert.equal(SH.needsEscalation(0.4, 0.5, 0.1), false); assert.equal(SH.needsEscalation(0.55, 0.5, 0.05), false); // tighter margin // the shipped default (ESC_MARGIN) is bench-set; pin the boundary behavior assert.equal(SH.needsEscalation(0.5 + SH.ESC_MARGIN, 0.5), false); assert.equal(SH.needsEscalation(0.5, 0.5), true); }); test("escalationPlan: confident clauses stay local, unsure or unscored go to Jev", () => { const s = stE(); s.rulesA = [{ enabled: true, word1: "cats", word2: "dogs" }]; s.rulesB = [{ enabled: true, word: "politics", above: true }]; // cats 0.9 (local), politics 0.52 (unsure → jev), E 0.05 vs cutoff 0.1 (unsure → jev) const plan = SH.escalationPlan(s, { p0: 0.9, n0: 0.52, e0: 0.05 }); assert.deepEqual(plan.local, ["A0"]); assert.deepEqual(plan.jev.sort(), ["B0", "E0"]); // no local scores at all → everything escalates (pre-SUX-7 behavior) const all = SH.escalationPlan(s, {}); assert.deepEqual(all.local, []); assert.deepEqual(all.jev.sort(), ["A0", "B0", "E0"]); // a confident E score (far from cutoff) stays local const eConf = SH.escalationPlan(s, { p0: 0.9, n0: 0.52, e0: 0.4 }); assert.deepEqual(eConf.local.sort(), ["A0", "E0"]); // disabled clauses never appear const s2 = stE(); s2.rulesA = [{ enabled: false, word1: "cats", word2: "dogs" }]; const none = SH.escalationPlan(s2, {}); assert.deepEqual(none.jev, ["E0"]); }); test("escalatedToLocalScale maps Jev's noul onto the cutoff scale", () => { assert.equal(SH.escalatedToLocalScale(0.5, 0.1), 0.1); // Jev unsure → at cutoff assert.equal(SH.escalatedToLocalScale(1, 0.1), 0.1 + SH.ESC_MARGIN); // → hide side assert.equal(SH.escalatedToLocalScale(0, 0.1), 0.1 - SH.ESC_MARGIN); // → show side // Jev's verdict survives decideFromScores: high noul hides, low noul shows const s = stE(); // effective cutoff 0.1 assert.equal(SH.decideFromScores(s, { e0: SH.escalatedToLocalScale(0.9, 0.1) }), "hide"); assert.equal(SH.decideFromScores(s, { e0: SH.escalatedToLocalScale(0.1, 0.1) }), "show"); }); test("buildQuestions(only=) restricts to the listed clause ids", () => { const s = stE(); s.rulesA = [{ enabled: true, word1: "cats", word2: "dogs" }]; s.rulesB = [{ enabled: true, word: "politics", above: true }]; const all = SH.buildQuestions(s); assert.deepEqual(Object.keys(all).sort(), ["compare0", "example0", "single0"]); assert.equal(all.example0.type, "noul"); // escalation round-trip: just the unsure clauses const some = SH.buildQuestions(s, ["B0", "E0"]); assert.deepEqual(Object.keys(some).sort(), ["example0", "single0"]); const justA = SH.buildQuestions(s, ["A0"]); assert.deepEqual(Object.keys(justA), ["compare0"]); }); test("buildRequest sends example rules' examples and counters to Jev", () => { const s = stE(); s.rulesE[0].counters = [{ text: "local bake sale", alt: "a cake" }]; const req = SH.buildRequest(s, { text: "vote tuesday", alt: "" }); assert.deepEqual(req.state.rule0, { examples: ["election news", "senate vote"], not_like: ["local bake sale [image: a cake]"], }); assert.match(req.questions.example0.instructions, /rule0\.examples/); // not asked → not sent const none = SH.buildRequest(s, { text: "x", alt: "" }, ["A0"]); assert.equal(none.state.rule0, undefined); }); test("jevToScores rescales escalated example answers onto the local scale", () => { const s = stE(); // effective cutoff 0.1 const hi = SH.jevToScores(s, { example0: { type: "noul", noul: 0.95 } }); const lo = SH.jevToScores(s, { example0: { type: "noul", noul: 0.05 } }); assert.ok(hi.e0 > 0.1 && hi.e0 < 0.1 + SH.ESC_MARGIN + 1e-9); assert.ok(lo.e0 < 0.1 && lo.e0 > 0.1 - SH.ESC_MARGIN - 1e-9); assert.equal(SH.decideFromScores(s, hi), "hide"); assert.equal(SH.decideFromScores(s, lo), "show"); // no example answer → no e0 → decide fails open assert.deepEqual(SH.jevToScores(s, {}), {}); }); /* ---------------- SUX-6: peek + corrections ---------------- */ test("normalizeSettings carries peek and rule-E counters with defaults", () => { const s = SH.normalizeSettings({ peek: true, rulesE: [{ enabled: true, examples: [{ text: "a" }], counters: [{ text: "b" }, { text: " " }] }], }); assert.equal(s.peek, true); assert.equal(s.rulesE[0].counters.length, 1); // blank dropped, like examples const d = SH.defaults(); assert.equal(d.peek, false); assert.deepEqual(d.rulesE, []); }); test("cacheKey for E reacts to counters", () => { const s = stE(); const base = SH.cacheKey(s, "hello", "", "m1@r1"); const withCnt = stE(); withCnt.rulesE[0].counters = [{ text: "actually not this", alt: "" }]; assert.notEqual(base, SH.cacheKey(withCnt, "hello", "", "m1@r1")); const diffCnt = stE(); diffCnt.rulesE[0].counters = [{ text: "different counter", alt: "" }]; assert.notEqual(SH.cacheKey(withCnt, "hello", "", "m1@r1"), SH.cacheKey(diffCnt, "hello", "", "m1@r1")); }); test("scoreExampleRule: counter-examples sharpen the not-this side, never replace the feed", () => { const ex = [[1, 0], [0.9, 0.1]]; const feed = [[0, 1]]; const post = [0.7, 0.7]; // ambiguous: between examples and feed const noCnt = SH.scoreExampleRule(post, ex, feed[0]); const cnt = SH.scoreExampleRule(post, ex, feed[0], [[0.71, 0.71]]); // counter ≈ the post assert.ok(cnt < noCnt, "counter near the post must lower its score"); // counter on the far side raises it (not-this side moves away) const farCnt = SH.scoreExampleRule(post, ex, feed[0], [[-1, 0]]); assert.ok(farCnt > noCnt); // no feed average: counters alone form the not-this side (still works) const cntOnly = SH.scoreExampleRule(post, ex, null, [[0.71, 0.71]]); assert.ok(cntOnly < SH.scoreExampleRule(post, ex, null)); // empty counters = old behavior exactly assert.equal(SH.scoreExampleRule(post, ex, feed[0], []), noCnt); }); test("hiddenByClause names the clause that hid the post", () => { const s = stE(); // E cutoff 0.1 s.rulesB = [{ enabled: true, word: "politics", above: true }]; // E hides (0.2 >= 0.1) → E named even though B also fails let h = SH.hiddenByClause(s, { e0: 0.2, n0: 0.3 }); assert.equal(h.type, "E"); assert.equal(h.index, 0); assert.equal(h.key, "e0"); assert.equal(h.score, 0.2); // B fails (0.3 <= 0.5), E passes → B named h = SH.hiddenByClause(s, { e0: 0.05, n0: 0.3 }); assert.equal(h.type, "B"); assert.equal(h.name, "politics"); // nothing hides → null assert.equal(SH.hiddenByClause(s, { e0: 0.05, n0: 0.9 }), null); // missing scores don't crash assert.equal(SH.hiddenByClause(s, {}), null); // rule A: p < 0.5 names the compare const sA = stE({ rulesE: [] }); sA.rulesA = [{ enabled: true, word1: "cats", word2: "dogs" }]; h = SH.hiddenByClause(sA, { p0: 0.3 }); assert.equal(h.type, "A"); assert.equal(h.name, "cats vs dogs"); }); /* ---------------- SUX-8: fuzzy-rule heuristic ---------------- */ test("ruleFuzziness: mean LOO recognition, null with <2 examples", () => { assert.equal(SH.ruleFuzziness([], null), null); assert.equal(SH.ruleFuzziness([[1, 0]], null), null); // tight cluster on one side of the feed: each example recognizes the rest const crisp = [[1, 0], [0.99, 0.01], [1, 0.01]]; const fCrisp = SH.ruleFuzziness(crisp, [-1, 0]); // scattered cluster: examples don't recognize each other const fuzzy = [[1, 0], [0, 1], [-1, 0]]; const fFuzzy = SH.ruleFuzziness(fuzzy, [0.7, 0.7]); assert.ok(fCrisp > fFuzzy); assert.ok(typeof fCrisp === "number" && typeof fFuzzy === "number"); // feed side dominates: same examples, feed at their centroid → low recognition const fNoContrast = SH.ruleFuzziness(crisp, SH.meanVector(crisp)); assert.ok(fNoContrast < fCrisp); }); test("needsJevForQuality flags low LOO recognition, never guesses", () => { // crisp: examples far from feed, tight → not fuzzy const crisp = [[1, 0], [0.99, 0.01], [1, 0.01]]; assert.equal(SH.needsJevForQuality(crisp, [-1, 0]), false); // unknown: too few examples → false (no warning until we can judge) assert.equal(SH.needsJevForQuality([[1, 0]], [-1, 0]), false); // fuzzy: examples average below the LOO threshold const low = [[0.1, 0], [0, 0.1], [-0.1, 0], [0, -0.1]]; assert.equal(SH.needsJevForQuality(low, SH.meanVector(low)), true); // threshold boundary respects FUZZY_LMEAN_THRESHOLD const t = SH.FUZZY_LMEAN_THRESHOLD; const feed = [0, 0]; const at = [[1, 0], [t - 0.5 + 1, 0], [1, 0]]; // hand-tuned below/above is fragile; check monotonicity instead assert.equal(typeof SH.needsJevForQuality(at, feed), "boolean"); }); /* ---------------- SUX-9: training word rules from Jev's answers ---------------- */ test("mode respects LOCAL_MODEL build switch", () => { if (SH.LOCAL_MODEL) { assert.equal(SH.normalizeSettings({ mode: "local" }).mode, "local"); assert.equal(SH.normalizeSettings({ mode: "jev" }).mode, "jev"); assert.equal(SH.normalizeSettings({ mode: "garbage" }).mode, "both"); } else { // switch off: every mode collapses to "both" (Jev-only without a runtime) assert.equal(SH.normalizeSettings({ mode: "local" }).mode, "both"); assert.equal(SH.normalizeSettings({ mode: "jev" }).mode, "both"); assert.equal(SH.normalizeSettings({ mode: "both" }).mode, "both"); } assert.equal(SH.defaults().mode, "both"); assert.equal(SH.modeAllowsJev("local"), false); assert.equal(SH.modeAllowsJev("jev"), true); assert.equal(SH.modeAllowsJev("both"), true); assert.equal(SH.modeAllowsLocal("jev"), false); assert.equal(SH.modeAllowsLocal("both"), true); }); test("model progress round-trips through the shared hook", () => { SH.reportModelProgress(42, "downloading"); const p = SH.getModelProgress(); assert.equal(p.pct, 42); assert.equal(p.status, "downloading"); SH.reportModelProgress(100, "ready"); assert.equal(SH.getModelProgress().pct, 100); // junk is clamped, not fatal SH.reportModelProgress(150, null); assert.equal(SH.getModelProgress().pct, 0); SH.reportModelProgress(100, "ready"); }); test("normalizeSettings carries trained separator data on word rules", () => { const s = SH.normalizeSettings({ rulesA: [{ enabled: true, word1: "cats", word2: "dogs", trained: { hide: [{ text: "a dog post" }], keep: [{ text: "a cat post" }] } }], rulesB: [{ enabled: true, word: "politics" }], }); assert.equal(s.rulesA[0].trained.hide.length, 1); assert.equal(s.rulesA[0].trained.keep.length, 1); assert.deepEqual(s.rulesB[0].trained, { hide: [], keep: [] }); assert.deepEqual(SH.defaults().rulesA, []); }); test("trainedSidesReady needs both sides and a minimum of posts", () => { assert.equal(SH.trainedSidesReady({ hide: [], keep: [] }), false); assert.equal(SH.trainedSidesReady({ hide: [[1, 0]], keep: [] }), false); assert.equal(SH.trainedSidesReady({ hide: [[1, 0]], keep: [[0, 1]] }), false); // only 2 assert.equal(SH.trainedSidesReady({ hide: [[1, 0], [0.9, 0.1]], keep: [[0, 1]] }), true); }); test("trainedSide maps confident Jev answers to separator sides, direction-aware", () => { const above = { above: true }; const below = { above: false }; assert.equal(SH.trainedSide("B", above, 0.9), "keep"); // mostly word, shown assert.equal(SH.trainedSide("B", above, 0.1), "hide"); // not word, hidden assert.equal(SH.trainedSide("B", below, 0.9), "hide"); // word → hidden ("are NOT") assert.equal(SH.trainedSide("B", below, 0.1), "keep"); assert.equal(SH.trainedSide("B", above, 0.5), null); // unsure → don't train assert.equal(SH.trainedSide("B", above, null), null); const aClause = {}; assert.equal(SH.trainedSide("A", aClause, 0.95), "keep"); // word1 assert.equal(SH.trainedSide("A", aClause, 0.05), "hide"); // word2 assert.equal(SH.trainedSide("A", aClause, 0.5), null); }); test("trainedToNative converts hide probability back to the clause scale", () => { // A: p = P(word1) = 1 - hideProb assert.equal(SH.trainedToNative(0, "A", {}), 1); assert.equal(SH.trainedToNative(1, "A", {}), 0); // B above ("are mostly"): n = P(word) = 1 - hideProb assert.equal(SH.trainedToNative(0.2, "B", { above: true }), 0.8); // B below ("are NOT"): n = P(word) = hideProb assert.equal(SH.trainedToNative(0.2, "B", { above: false }), 0.2); }); test("trainedHideProb separates hide-side from keep-side posts", () => { const trained = { hide: [[1, 0], [0.95, 0.05]], // politics-like keep: [[-1, 0], [0, -1]], // not-politics-like }; const feed = [0, 1]; const pHideSide = SH.trainedHideProb([1, 0.02], trained, feed); const pKeepSide = SH.trainedHideProb([-0.98, 0], trained, feed); assert.ok(pHideSide > 0.9, `hide-side post should hide, got ${pHideSide}`); assert.ok(pKeepSide < 0.1, `keep-side post should show, got ${pKeepSide}`); // trained scores land outside the SUX-7 band → local, no Jev call assert.equal(SH.needsEscalation(pHideSide, 0.5), false); assert.equal(SH.needsEscalation(pKeepSide, 0.5), false); }); test("looCutoff: the lowest leave-one-out score minus margin", () => { // 4 points of a tight cluster on the unit circle-ish plane const ex = [[1, 0], [0.98, 0.02], [1, 0.01], [0.99, -0.01]]; const cut = SH.looCutoff(ex, null, 0.05); // LOO score of each ≈ cos to the mean of the others ≈ ~1; cutoff just under it assert.ok(cut > 0.9, `cutoff ${cut}`); assert.ok(cut <= 1 - 0.05); // no feed average and no margin → the raw lowest LOO score const raw = SH.looCutoff(ex, null, 0); assert.ok(raw > cut); // fewer than 2 examples → no automatic cutoff assert.equal(SH.looCutoff([[1, 0]], null, 0.05), null); assert.equal(SH.looCutoff([], null, 0.05), null); }); test("looCutoff separates a straggler example from a tight cluster", () => { const ex = [[1, 0], [1, 0.01], [0.99, 0.02], [0, 1]]; // one outlier const cut = SH.looCutoff(ex, null, 0); // the outlier scored against the cluster ≈ 0 → cutoff ≈ 0 assert.ok(Math.abs(cut) < 0.1, `cutoff ${cut}`); }); test("effectiveCutoff: slider at 0.5 is the stored value, ends shift by ±span", () => { const c = { cutoff: 0.2, strictness: 0.5 }; assert.ok(Math.abs(SH.effectiveCutoff(c) - 0.2) < 1e-12); assert.ok(Math.abs(SH.effectiveCutoff({ cutoff: 0.2, strictness: 0 }) - (0.2 - SH.STRICTNESS_SPAN)) < 1e-12); assert.ok(Math.abs(SH.effectiveCutoff({ cutoff: 0.2, strictness: 1 }) - (0.2 + SH.STRICTNESS_SPAN)) < 1e-12); // missing fields → cutoff 0, slider 0.5 → 0 assert.equal(SH.effectiveCutoff({}), 0); }); test("decideFromScores honors strictness through effectiveCutoff", () => { const s = stE(); // cutoff 0.1, strictness 0.5 → effective 0.1 assert.equal(SH.decideFromScores(s, { e0: 0.1 }), "hide"); // same score, stricter → above the shifted cutoff → shows const strict = stE(); strict.rulesE[0].strictness = 1; // effective 0.1 + SPAN assert.equal(SH.decideFromScores(strict, { e0: 0.1 }), "show"); assert.equal(SH.decideFromScores(strict, { e0: 0.26 }), "hide"); // looser → cutoff 0.1 − SPAN const loose = stE(); loose.rulesE[0].strictness = 0; assert.equal(SH.decideFromScores(loose, { e0: 0.1 - SH.STRICTNESS_SPAN + 0.01 }), "hide"); assert.equal(SH.decideFromScores(loose, { e0: 0.1 - SH.STRICTNESS_SPAN - 0.01 }), "show"); }); /* ---------------- batching + per-clause cache ---------------- */ test("buildBatchRequest puts each post under its own state path", () => { const s = SH.normalizeSettings({ enabled: true, rulesA: [{ enabled: true, word1: "cats", word2: "dogs" }], rulesB: [{ enabled: true, word: "politics", above: true }], }); const req = SH.buildBatchRequest(s, [ { post: { text: "one", alt: "" } }, { post: { text: "two", alt: "x" }, only: ["B0"] }, ]); assert.deepEqual(req.state.post0, { text: "one", alt: "" }); assert.deepEqual(req.state.post1, { text: "two", alt: "x" }); assert.deepEqual(Object.keys(req.questions).sort(), ["j0_compare0", "j0_single0", "j1_single0"]); assert.match(req.questions.j1_single0.instructions, /`post1`/); assert.doesNotMatch(req.questions.j1_single0.instructions, /`post`/); }); test("buildBatchRequest sends each example rule's examples once", () => { const s = stE(); const req = SH.buildBatchRequest(s, [{ post: { text: "a" } }, { post: { text: "b" } }]); assert.deepEqual(req.state.rule0.examples, ["election news", "senate vote"]); assert.ok(req.questions.j0_example0 && req.questions.j1_example0); }); test("splitBatchAnswers undoes the j_ prefix", () => { const split = SH.splitBatchAnswers({ j0_single0: { noul: 0.9 }, j1_single0: { noul: 0.1 }, j2_single0: { noul: 0.5 }, junk: {}, }, 2); assert.deepEqual(split, [{ single0: { noul: 0.9 } }, { single0: { noul: 0.1 } }]); assert.deepEqual(SH.splitBatchAnswers(null, 1), [{}]); }); test("clauseScoreKeys: one key per clause, only that clause's config in it", () => { const mk = (patch) => SH.normalizeSettings(Object.assign({ enabled: true, rulesA: [{ enabled: true, word1: "cats", word2: "dogs" }], rulesB: [{ enabled: true, word: "politics", above: true }], }, patch)); const base = SH.clauseScoreKeys(mk(), "hello", ""); assert.deepEqual(base.map((c) => c.id + c.score), ["A0p0", "B0n0"]); // direction and combinator are decide-time: same keys const flipped = SH.clauseScoreKeys(mk({ rulesB: [{ enabled: true, word: "politics", above: false }], combine: "or" }), "hello", ""); assert.deepEqual(flipped.map((c) => c.key), base.map((c) => c.key)); // editing B leaves A's key alone const edited = SH.clauseScoreKeys(mk({ rulesB: [{ enabled: true, word: "sports", above: true }] }), "hello", ""); assert.equal(edited[0].key, base[0].key); assert.notEqual(edited[1].key, base[1].key); // content and model id matter assert.notEqual(SH.clauseScoreKeys(mk(), "bye", "")[0].key, base[0].key); assert.notEqual(SH.clauseScoreKeys(mk(), "hello", "", "m1")[0].key, base[0].key); // disabled clauses get no key assert.equal(SH.clauseScoreKeys(mk({ rulesA: [{ enabled: false, word1: "a", word2: "b" }] }), "x", "").length, 1); });