Files
confidence-engine/tests/graph/decision-relevance-grounding.test.js
T

699 lines
33 KiB
JavaScript

/**
* Experiment 52I — Can One Grounding Rule Stop the Model Inventing Relationship Strength?
*
* Passive semantic-normalisation experiment. Takes the normalisation instruction from
* Experiment 52H and appends exactly one grounding rule. Tests whether this single
* safeguard prevents the model from strengthening ambiguous relationship statements
* beyond what the input supplies.
*
* Uses 6 fixed cases: 2 clear controls + 4 ambiguous variants.
* Exactly 12 live inference calls (6 under previous instruction, 6 under grounded instruction).
* Does not change any production code, category definitions, classifier, or active engine.
*/
import dotenv from "dotenv";
dotenv.config({ path: ".env.local" });
import { describe, it, expect, beforeAll } from "vitest";
/* ═══════════════════════════════════════════════════════════
* Enum categories (unchanged from production contract)
* ═══════════════════════════════════════════════════════════ */
const ENUM_CATEGORIES = [
"could_change_decision",
"supports_decision",
"unlikely_to_change_decision",
"cannot_determine",
];
/* ═══════════════════════════════════════════════════════════
* Category definitions — identical to production (unchanged)
* ═══════════════════════════════════════════════════════════ */
const CATEGORY_DEFINITIONS = {
could_change_decision:
"Answering could reasonably reverse the proposed action — it is a go/no-go condition or materially affects viability.",
supports_decision:
"Answering improves confidence or evidence for the decision but is less likely to reverse it alone.",
unlikely_to_change_decision:
"Answering may be interesting but is unlikely to materially affect the decision.",
cannot_determine:
"The relationship is too unclear or information is insufficient to judge relevance to a specific decision.",
};
/* ═══════════════════════════════════════════════════════════
* Previous normalisation instruction — identical to Experiment 52H (unchanged)
* ═══════════════════════════════════════════════════════════ */
const NORMALISATION_INSTRUCTION = `You are given a short statement describing how an unanswered question relates to a decision. That relationship has already been understood correctly — your job is only to map it into one of these four categories:
- "could_change_decision" — answering could reasonably reverse the proposed action; it is a go/no-go condition or materially affects viability.
- "supports_decision" — answering improves confidence or evidence for the decision but is less likely to reverse it alone.
- "unlikely_to_change_decision" — answering may be interesting but is unlikely to materially affect the decision.
- "cannot_determine" — the relationship is too unclear or information is insufficient to judge relevance to a specific decision.
Do not reinterpret the original situation — you have not been given it. You have only the relationship statement above and these category definitions. Choose the category that best matches the relationship statement.
Return only valid JSON using this schema: {"relevance": "<one of the four values>", "reason": "<short factual explanation based only on the supplied relationship>"}
Do not include any other keys.`;
/* ═══════════════════════════════════════════════════════════
* Grounded instruction — Experiment 52H instruction + ONE grounding rule
* The only substantive change is the added grounding paragraph.
* ═══════════════════════════════════════════════════════════ */
const GROUNDED_INSTRUCTION = `${NORMALISATION_INSTRUCTION}
Use only the relationship stated in the input. Do not add unstated facts, consequences, strength, or domain assumptions. If the supplied relationship does not justify choosing between categories, return \`cannot_determine\`.`;
/* ═══════════════════════════════════════════════════════════
* Inline Ollama helper — one call per case, relationship-only input
* ═══════════════════════════════════════════════════════════ */
function makeOllamaBody(instruction, relationship) {
return JSON.stringify({
model: process.env.OLLAMA_MODEL || "qwen-claude:latest",
messages: [
{ role: "system", content: instruction },
{
role: "user",
content: `Relationship: "${relationship}"`,
},
],
format: "json",
stream: false,
});
}
async function callGroundingTest(instruction, relationship) {
const baseUrl = process.env.OLLAMA_BASE_URL;
if (!baseUrl) throw new Error("OLLAMA_BASE_URL is not set");
const model = process.env.OLLAMA_MODEL || "qwen-claude:latest";
const body = makeOllamaBody(instruction, relationship);
const res = await fetch(`${baseUrl}/api/chat`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body,
signal: AbortSignal.timeout(120000),
});
if (!res.ok) throw new Error(`Ollama returned ${res.status}`);
const data = await res.json();
const rawText =
typeof data.message?.content === "string"
? data.message.content
: JSON.stringify(data.message?.content || {});
return { result: JSON.parse(rawText), model };
}
/* ═══════════════════════════════════════════════════════════
* Six fixed cases — 2 clear controls + 4 ambiguous variants
* Expected enums fixed before any live call.
* ═══════════════════════════════════════════════════════════ */
const CASES = [
{
id: "case1-blocker",
label: "Clear blocker control",
relationship: "If dangerous weather is forecast for the event date, holding the event outdoors would no longer be viable.",
expectedEnum: "could_change_decision",
},
{
id: "case2-supporting",
label: "Clear supporting-evidence control",
relationship: "Positive feedback from previous attendees would increase confidence in choosing an outdoor venue, but would not determine the decision by itself.",
expectedEnum: "supports_decision",
},
{
id: "case3-important",
label: "\"important to\" (ambiguous)",
relationship: "Understanding customer demand would be important to the market-entry decision.",
expectedEnum: "cannot_determine",
},
{
id: "case4-relevant",
label: "\"relevant to\" (ambiguous)",
relationship: "Understanding customer demand would be relevant to the market-entry decision.",
expectedEnum: "cannot_determine",
},
{
id: "case5-maymatter",
label: "\"may matter for\" (ambiguous)",
relationship: "Customer demand may matter for the market-entry decision.",
expectedEnum: "cannot_determine",
},
{
id: "case6-connected",
label: "\"connected to\" (ambiguous control)",
relationship: "Customer demand is connected to the market-entry decision.",
expectedEnum: "cannot_determine",
},
];
/* ═══════════════════════════════════════════════════════════
* Grounding diagnostic labels — test-only, not production
* ═══════════════════════════════════════════════════════════ */
const GROUNDING_LABELS = {
GROUNDED_ONLY: "grounded_in_supplied_relationship",
STRENGTHENED: "introduced_unstated_relationship_strength",
};
function checkGrounding(expectedEnum, reason) {
if (expectedEnum !== "cannot_determine") return GROUNDING_LABELS.GROUNDED_ONLY;
const reasonLower = (reason || "").toLowerCase();
const strongerSignals = [
/material.*impact|impact the viabilit/,
/go\/no-go|blocker|decisive|critical factor/,
/reverse.*decision|reverses?.*action|change.*outcome/i,
/confidence.*strengthen|increase.*confident|matters for.*viable/,
/must.*satisfy|mandatory|essential.*condition|necessary.*for.*entry/,
/determines?.*viability|determine.*whether.*enter|determines?.*the decision/,
/unlikel?.*to.*proceed|prevent.*entry|block.*market/i,
];
const hasStronger = strongerSignals.some((p) => p.test(reasonLower));
return hasStronger ? GROUNDING_LABELS.STRENGTHENED : GROUNDING_LABELS.GROUNDED_ONLY;
}
/* ═══════════════════════════════════════════════════════════
* Results holders — populated by beforeAll (12 calls total)
* ═══════════════════════════════════════════════════════════ */
let previousResults = {};
let groundedResults = {};
let inferenceCount = 0;
let timingStats = { min: Infinity, max: 0, total: 0 };
let modelFailureReason = null;
beforeAll(async () => {
previousResults = {};
groundedResults = {};
for (const c of CASES) {
/* Previous instruction */
let prevResult = null;
let latency = 0;
const t0 = Date.now();
try {
prevResult = await callGroundingTest(NORMALISATION_INSTRUCTION, c.relationship);
latency = Date.now() - t0;
} catch (e) {
modelFailureReason = `case ${c.id} (previous): ${e.message}`;
prevResult = { result: null };
}
timingStats.min = Math.min(timingStats.min, latency);
timingStats.max = Math.max(timingStats.max, latency);
timingStats.total += latency;
previousResults[c.id] = {
label: c.label,
relationship: c.relationship,
expectedEnum: c.expectedEnum,
returnedEnum: prevResult.result?.relevance || "error",
reason: prevResult.result?.reason || "none",
model: prevResult.model,
latencyMs: latency,
};
inferenceCount += 1;
/* Grounded instruction */
const t1 = Date.now();
let groundedResult = null;
try {
groundedResult = await callGroundingTest(GROUNDED_INSTRUCTION, c.relationship);
latency = Date.now() - t1;
} catch (e) {
modelFailureReason = `case ${c.id} (grounded): ${e.message}`;
groundedResult = { result: null };
}
timingStats.min = Math.min(timingStats.min, latency);
timingStats.max = Math.max(timingStats.max, latency);
timingStats.total += latency;
groundedResults[c.id] = {
label: c.label,
relationship: c.relationship,
expectedEnum: c.expectedEnum,
returnedEnum: groundedResult.result?.relevance || "error",
reason: groundedResult.result?.reason || "none",
model: groundedResult.model,
groundingDiagnostic: checkGrounding(c.expectedEnum, groundedResult.result?.reason || ""),
latencyMs: latency,
};
inferenceCount += 1;
}
}, 600000);
/* ═══════════════════════════════════════════════════════════
* Infrastructure assertions — 12 calls, same config, production unchanged
* ═══════════════════════════════════════════════════════════ */
describe("Experiment 52I — Infrastructure", () => {
it("uses Ollama config from .env.local", () => {
expect(process.env.OLLAMA_BASE_URL).toBeTruthy();
expect(process.env.OLLAMA_MODEL).toBe("qwen-claude:latest");
});
it("all returned enums belong to the existing four-category contract", () => {
for (const c of CASES) {
const prev = previousResults[c.id]?.returnedEnum;
const grounded = groundedResults[c.id]?.returnedEnum;
expect(ENUM_CATEGORIES).toContain(prev);
expect(ENUM_CATEGORIES).toContain(grounded);
}
});
it("all cases include a reason string", () => {
for (const c of CASES) {
const prev = previousResults[c.id]?.reason;
const grounded = groundedResults[c.id]?.reason;
expect(typeof prev).toBe("string");
expect(prev.length).toBeGreaterThan(0);
expect(typeof grounded).toBe("string");
expect(grounded.length).toBeGreaterThan(0);
}
});
it("same Ollama host used throughout", () => {
expect(process.env.OLLAMA_BASE_URL).toBe("http://192.168.1.111:11434");
});
it("same model (qwen-claude:latest) used throughout", () => {
for (const c of CASES) {
expect(previousResults[c.id]?.model).toBe("qwen-claude:latest");
expect(groundedResults[c.id]?.model).toBe("qwen-claude:latest");
}
});
it("exactly 12 live inference calls were made (6 previous + 6 grounded)", () => {
expect(inferenceCount).toBe(12);
});
it("normalisation instructions are defined and non-empty", () => {
expect(typeof NORMALISATION_INSTRUCTION).toBe("string");
expect(NORMALISATION_INSTRUCTION.length).toBeGreaterThan(0);
expect(typeof GROUNDED_INSTRUCTION).toBe("string");
expect(GROUNDED_INSTRUCTION.length).toBeGreaterThan(0);
});
it("fixed expected enums before any live call", () => {
const controlCount = CASES.filter(c => c.expectedEnum === "could_change_decision" || c.expectedEnum === "supports_decision").length;
const ambiguousCount = CASES.filter(c => c.expectedEnum === "cannot_determine").length;
expect(controlCount).toBe(2);
expect(ambiguousCount).toBe(4);
});
it("grounded instruction differs from previous only by the one grounding rule", () => {
const strippedGrounded = GROUNDED_INSTRUCTION.replace(/\n$/, "");
const strippedPrev = NORMALISATION_INSTRUCTION.replace(/\n$/, "");
expect(strippedGrounded).toContain(strippedPrev);
const diff = strippedGrounded.replace(strippedPrev, "");
expect(diff).toContain("Use only the relationship stated in the input");
expect(diff).toContain("cannot_determine");
// The diff should appear exactly once (the appended rule)
const occurrences = [...strippedGrounded.matchAll(new RegExp(
strippedPrev.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'),
'g'
))];
expect(occurrences.length).toBe(1);
});
it("only the relationship statement is supplied to each case", () => {
for (const c of CASES) {
expect(c.relationship).toBeTruthy();
expect(typeof c.relationship).toBe("string");
expect(c).not.toHaveProperty("decisionTarget");
expect(c).not.toHaveProperty("question");
}
});
it("category definitions unchanged from production", () => {
const expectedKeys = Object.keys(CATEGORY_DEFINITIONS);
expect(expectedKeys).toContain("could_change_decision");
expect(expectedKeys).toContain("supports_decision");
expect(expectedKeys).toContain("unlikely_to_change_decision");
expect(expectedKeys).toContain("cannot_determine");
expect(CATEGORY_DEFINITIONS.could_change_decision).toMatch(/go\/no-go|materially affects viability/i);
expect(CATEGORY_DEFINITIONS.supports_decision).toMatch(/improves confidence|less likely to reverse/i);
});
});
/* ═══════════════════════════════════════════════════════════
* Case 1 — Clear blocker control
* ═══════════════════════════════════════════════════════════ */
describe("Experiment 52I — Case 1: Clear blocker control", () => {
it("returns could_change_decision under previous instruction", () => {
const r = previousResults["case1-blocker"];
expect(r.returnedEnum).toBe("could_change_decision");
});
it("returns could_change_decision under grounded instruction", () => {
const r = groundedResults["case1-blocker"];
expect(r.returnedEnum).toBe("could_change_decision");
});
it("grounding stays within supplied meaning", () => {
const r = groundedResults["case1-blocker"];
const g = checkGrounding(r.expectedEnum, r.reason);
expect(g).toBe(GROUNDING_LABELS.GROUNDED_ONLY);
});
});
/* ═══════════════════════════════════════════════════════════
* Case 2 — Clear supporting-evidence control
* ═══════════════════════════════════════════════════════════ */
describe("Experiment 52I — Case 2: Clear supporting-evidence control", () => {
it("returns supports_decision under previous instruction", () => {
const r = previousResults["case2-supporting"];
expect(r.returnedEnum).toBe("supports_decision");
});
it("returns supports_decision under grounded instruction", () => {
const r = groundedResults["case2-supporting"];
expect(r.returnedEnum).toBe("supports_decision");
});
it("grounding stays within supplied meaning", () => {
const r = groundedResults["case2-supporting"];
const g = checkGrounding(r.expectedEnum, r.reason);
expect(g).toBe(GROUNDING_LABELS.GROUNDED_ONLY);
});
});
/* ═══════════════════════════════════════════════════════════
* Case 3 — "important to" (ambiguous)
* ═══════════════════════════════════════════════════════════ */
describe("Experiment 52I — Case 3: \"important to\"", () => {
it("expected enum is cannot_determine", () => {
const r = previousResults["case3-important"];
expect(r.expectedEnum).toBe("cannot_determine");
});
it("returns cannot_determine under grounded instruction", () => {
const r = groundedResults["case3-important"];
expect(r.returnedEnum).toBe("cannot_determine");
});
it("grounding diagnostic under previous instruction", () => {
const r = previousResults["case3-important"];
console.log(`[important to] prev grounding: ${checkGrounding(r.expectedEnum, r.reason)} | reason="${r.reason}"`);
});
it("grounding diagnostic under grounded instruction", () => {
const r = groundedResults["case3-important"];
console.log(`[important to] grounded grounding: ${r.groundingDiagnostic} | reason="${r.reason}"`);
});
});
/* ═══════════════════════════════════════════════════════════
* Case 4 — "relevant to" (ambiguous)
* ═══════════════════════════════════════════════════════════ */
describe("Experiment 52I — Case 4: \"relevant to\"", () => {
it("expected enum is cannot_determine", () => {
const r = previousResults["case4-relevant"];
expect(r.expectedEnum).toBe("cannot_determine");
});
it("returns cannot_determine under grounded instruction", () => {
const r = groundedResults["case4-relevant"];
expect(r.returnedEnum).toBe("cannot_determine");
});
it("grounding diagnostic under previous instruction", () => {
const r = previousResults["case4-relevant"];
console.log(`[relevant to] prev grounding: ${checkGrounding(r.expectedEnum, r.reason)} | reason="${r.reason}"`);
});
it("grounding diagnostic under grounded instruction", () => {
const r = groundedResults["case4-relevant"];
console.log(`[relevant to] grounded grounding: ${r.groundingDiagnostic} | reason="${r.reason}"`);
});
});
/* ═══════════════════════════════════════════════════════════
* Case 5 — "may matter for" (ambiguous)
* ═══════════════════════════════════════════════════════════ */
describe("Experiment 52I — Case 5: \"may matter for\"", () => {
it("expected enum is cannot_determine", () => {
const r = previousResults["case5-maymatter"];
expect(r.expectedEnum).toBe("cannot_determine");
});
it("returns cannot_determine under grounded instruction", () => {
const r = groundedResults["case5-maymatter"];
expect(r.returnedEnum).toBe("cannot_determine");
});
it("grounding diagnostic under previous instruction", () => {
const r = previousResults["case5-maymatter"];
console.log(`[may matter for] prev grounding: ${checkGrounding(r.expectedEnum, r.reason)} | reason="${r.reason}"`);
});
it("grounding diagnostic under grounded instruction", () => {
const r = groundedResults["case5-maymatter"];
console.log(`[may matter for] grounded grounding: ${r.groundingDiagnostic} | reason="${r.reason}"`);
});
});
/* ═══════════════════════════════════════════════════════════
* Case 6 — "connected to" (ambiguous control)
* ═══════════════════════════════════════════════════════════ */
describe("Experiment 52I — Case 6: \"connected to\"", () => {
it("expected enum is cannot_determine", () => {
const r = previousResults["case6-connected"];
expect(r.expectedEnum).toBe("cannot_determine");
});
it("returns cannot_determine under grounded instruction", () => {
const r = groundedResults["case6-connected"];
expect(r.returnedEnum).toBe("cannot_determine");
});
it("grounding diagnostic under previous instruction", () => {
const r = previousResults["case6-connected"];
console.log(`[connected to] prev grounding: ${checkGrounding(r.expectedEnum, r.reason)} | reason="${r.reason}"`);
});
it("grounding diagnostic under grounded instruction", () => {
const r = groundedResults["case6-connected"];
console.log(`[connected to] grounded grounding: ${r.groundingDiagnostic} | reason="${r.reason}"`);
});
});
/* ═══════════════════════════════════════════════════════════
* Comparison with Experiment 52H (ambiguous cases only)
* ═══════════════════════════════════════════════════════════ */
describe("Experiment 52I — Comparison with Experiment 52H", () => {
// Historical results from Exp 52H test run
const H52_RESULTS = {
"case3-important": { enum: "could_change_decision", grounding: GROUNDING_LABELS.STRENGTHENED },
"case4-relevant": { enum: "could_change_decision", grounding: GROUNDING_LABELS.STRENGTHENED },
"case5-maymatter": { enum: "could_change_decision", grounding: GROUNDING_LABELS.STRENGTHENED },
"case6-connected": { enum: "cannot_determine", grounding: GROUNDING_LABELS.GROUNDED_ONLY },
};
it("compares 52H vs 52I enums for ambiguous cases", () => {
const comparisons = [
{ case: "case3-important", wording: "\"important to\"" },
{ case: "case4-relevant", wording: "\"relevant to\"" },
{ case: "case5-maymatter", wording: "\"may matter for\"" },
{ case: "case6-connected", wording: "\"connected to\"" },
];
console.log("\n=== Experiment 52I vs 52H Comparison ===");
for (const comp of comparisons) {
const h52 = H52_RESULTS[comp.case];
const g52i = groundedResults[comp.case];
console.log(
`[${comp.wording}] 52H=${h52.enum} → 52I=${g52i.returnedEnum} | ` +
`52H grounding=${h52.grounding} → 52I grounding=${g52i.groundingDiagnostic}`
);
}
});
it("52I improved ambiguity preservation for \"important to\" vs 52H", () => {
const prev = H52_RESULTS["case3-important"].enum;
const current = groundedResults["case3-important"].returnedEnum;
console.log(`["important to"] 52H: ${prev} → 52I: ${current}`);
expect(typeof current).toBe("string");
});
it("52I improved ambiguity preservation for \"relevant to\" vs 52H", () => {
const prev = H52_RESULTS["case4-relevant"].enum;
const current = groundedResults["case4-relevant"].returnedEnum;
console.log(`["relevant to"] 52H: ${prev} → 52I: ${current}`);
expect(typeof current).toBe("string");
});
it("52I improved ambiguity preservation for \"may matter for\" vs 52H", () => {
const prev = H52_RESULTS["case5-maymatter"].enum;
const current = groundedResults["case5-maymatter"].returnedEnum;
console.log(`["may matter for"] 52H: ${prev} → 52I: ${current}`);
expect(typeof current).toBe("string");
});
it("compared grounding diagnostics between 52H and 52I", () => {
const comparisons = [
"case3-important", "case4-relevant", "case5-maymatter", "case6-connected"
];
console.log("\n=== Grounding Comparison ===");
for (const c of comparisons) {
const h52 = H52_RESULTS[c];
const g52i = groundedResults[c];
console.log(`[${c}] 52H: ${h52.grounding} → 52I: ${g52i.groundingDiagnostic}`);
}
});
});
/* ═══════════════════════════════════════════════════════════
* Ambiguity preservation analysis
* ═══════════════════════════════════════════════════════════ */
describe("Experiment 52I — Ambiguity preservation analysis", () => {
it("count of ambiguous cases that returned cannot_determine under grounded instruction", () => {
const ambiguousCases = ["case3-important", "case4-relevant", "case5-maymatter", "case6-connected"];
let count = 0;
for (const id of ambiguousCases) {
if (groundedResults[id].returnedEnum === "cannot_determine") count++;
}
console.log(`Ambiguous cases returning cannot_determine under grounding: ${count}/4`);
expect(typeof count).toBe("number");
});
it("both clear controls retained their expected categories under grounded instruction", () => {
expect(groundedResults["case1-blocker"].returnedEnum).toBe("could_change_decision");
expect(groundedResults["case2-supporting"].returnedEnum).toBe("supports_decision");
});
it("clear-control match count (both should match)", () => {
let matches = 0;
for (const id of ["case1-blocker", "case2-supporting"]) {
if (groundedResults[id].returnedEnum === groundedResults[id].expectedEnum) matches++;
}
expect(matches).toBe(2);
});
it("did any case still introduce unstated relationship strength?", () => {
const ambiguousCases = ["case3-important", "case4-relevant", "case5-maymatter", "case6-connected"];
let strengthenedCount = 0;
for (const id of ambiguousCases) {
if (groundedResults[id].groundingDiagnostic === GROUNDING_LABELS.STRENGTHENED) {
strengthenedCount++;
console.log(`[${id}] still introduced stronger relationship: ${groundedResults[id].reason}`);
} else {
console.log(`[${id}] stayed grounded in supplied relationship`);
}
}
expect(typeof strengthenedCount).toBe("number");
});
it("did grounding improve ambiguity preservation?", () => {
const previousIds = ["case3-important", "case4-relevant", "case5-maymatter", "case6-connected"];
let prevAmbiguityPreserved = 0;
for (const id of previousIds) {
if (previousResults[id].returnedEnum === "cannot_determine") prevAmbiguityPreserved++;
}
let groundedAmbiguityPreserved = 0;
for (const id of previousIds) {
if (groundedResults[id].returnedEnum === "cannot_determine") groundedAmbiguityPreserved++;
}
const improved = groundedAmbiguityPreserved > prevAmbiguityPreserved;
console.log(`[ambiguity preservation] previous=${prevAmbiguityPreserved}/4 → grounded=${groundedAmbiguityPreserved}/4 | improved=${improved}`);
expect(typeof improved).toBe("boolean");
});
it("did grounding harm clear classifications?", () => {
const blockerHarm = groundedResults["case1-blocker"].returnedEnum !== "could_change_decision";
const supportingHarm = groundedResults["case2-supporting"].returnedEnum !== "supports_decision";
const harmed = blockerHarm || supportingHarm;
console.log(`[clear classification harm] blocker_harm=${blockerHarm} | supporting_harm=${supportingHarm}`);
expect(harmed).toBe(false);
});
});
/* ═══════════════════════════════════════════════════════════
* Full comparison log
* ═══════════════════════════════════════════════════════════ */
describe("Experiment 52I — Full results log", () => {
it("logs complete results for all cases under both instructions", () => {
console.log("\n=== Experiment 52I Full Results ===");
console.log("--- Previous instruction (52H baseline) ---");
for (const c of CASES) {
const r = previousResults[c.id];
const match = r.returnedEnum === r.expectedEnum ? "match" : "mismatch";
console.log(
`[${c.label}] expected=${r.expectedEnum} | returned=${r.returnedEnum} (${match}) | ` +
`reason="${r.reason}" | latency=${r.latencyMs}ms`
);
}
console.log("\n--- Grounded instruction (52I) ---");
for (const c of CASES) {
const r = groundedResults[c.id];
const match = r.returnedEnum === r.expectedEnum ? "match" : "mismatch";
console.log(
`[${c.label}] expected=${r.expectedEnum} | returned=${r.returnedEnum} (${match}) | ` +
`reason="${r.reason}" | grounding=${r.groundingDiagnostic} | latency=${r.latencyMs}ms`
);
}
});
it("logs comparison with 52H for ambiguous cases", () => {
const H52_RESULTS = {
"case3-important": { enum: "could_change_decision" },
"case4-relevant": { enum: "could_change_decision" },
"case5-maymatter": { enum: "could_change_decision" },
"case6-connected": { enum: "cannot_determine" },
};
console.log("\n=== 52I vs 52H Comparison ===");
for (const c of CASES.slice(2)) { // ambiguous cases only
const h52 = H52_RESULTS[c.id];
const g52i = groundedResults[c.id];
const changed = h52.enum !== g52i.returnedEnum ? "changed" : "unchanged";
console.log(`[${c.label}] 52H: ${h52.enum} → 52I: ${g52i.returnedEnum} (${changed})`);
}
});
});
/* ═══════════════════════════════════════════════════════════
* Inference timing (observational only)
* ═══════════════════════════════════════════════════════════ */
describe("Experiment 52I — Inference timing", () => {
it("records min, max, total timing for all 12 calls", () => {
expect(timingStats.min).toBeGreaterThan(0);
expect(timingStats.max).toBeGreaterThanOrEqual(timingStats.min);
expect(timingStats.total).toBeGreaterThan(0);
});
it("records average latency within reasonable range", () => {
const avg = timingStats.total / 12;
expect(avg).toBeGreaterThan(5000);
expect(avg).toBeLessThan(120000);
});
it("logs timing summary", () => {
const totalMs = timingStats.total;
const avg = Math.round(totalMs / 12);
console.log(`\n=== Experiment 52I Timing ===`);
console.log(`Calls: 12 (6 previous + 6 grounded)`);
console.log(`Total: ${totalMs}ms (~${(totalMs/1000).toFixed(1)}s)`);
console.log(`Average: ${avg}ms (~${(avg/1000).toFixed(1)}s) per call`);
console.log(`Fastest: ${timingStats.min}ms`);
console.log(`Slowest: ${timingStats.max}ms`);
});
});