experiment: assess decision condition status
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
/**
|
||||
* Experiment 23 — Decision Condition Status Assessment.
|
||||
*
|
||||
* Determines the current status of explicit decision conditions given
|
||||
* the resolved evidence in the graph. A pure passive layer that reads
|
||||
* only existing node fields and edges. No new graph structure, no LLM
|
||||
* calls, no mutation.
|
||||
*
|
||||
* Classification rules (evaluated in order):
|
||||
* 1. cannot_determine — condition text is missing or graph is incomplete.
|
||||
* 2. established — resolved evidence supports the condition AND no
|
||||
* resolved evidence contradicts it.
|
||||
* 3. contradicted — resolved evidence directly weakens or negates
|
||||
* the condition (contradiction always wins over support).
|
||||
* 4. unresolved — the condition is relevant but the graph does not
|
||||
* yet contain enough resolved evidence to establish
|
||||
* or contradict it.
|
||||
*/
|
||||
|
||||
/* ── Decision concept groups for generic matching ──────────── */
|
||||
|
||||
const CONDITION_GROUPS = {
|
||||
demand: {
|
||||
support: ["demand", "need", "interest", "customers", "audience"],
|
||||
contradiction: [],
|
||||
},
|
||||
compliance: {
|
||||
support: ["compliance", "regulation", "legal", "required", "mandatory", "gdpr", "data residency"],
|
||||
contradiction: [],
|
||||
},
|
||||
value_cost: {
|
||||
support: ["cost", "investment", "justif", "viability", "financial", "revenue", "budget"],
|
||||
contradiction: [],
|
||||
},
|
||||
differentiation: {
|
||||
support: ["differentiat", "advantage", "competit", "positioning", "superior", "unique"],
|
||||
contradiction: [],
|
||||
},
|
||||
};
|
||||
|
||||
/** Contradiction phrases — any resolved node text matching one of these weakens a condition */
|
||||
|
||||
const CONTRADICTION_PHRASES = [
|
||||
"does not support",
|
||||
"cannot meet",
|
||||
"unreachable",
|
||||
"not achievable",
|
||||
"impossible to achieve",
|
||||
"no comparable",
|
||||
];
|
||||
|
||||
/* ── Helpers ──────────────────────────────────────────────── */
|
||||
|
||||
function normalise(value) {
|
||||
return String(value || "").toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
|
||||
}
|
||||
|
||||
function collectResolvedNodeTexts(graph) {
|
||||
const resolvedIds = new Set(graph?.resolvedNodeIds || []);
|
||||
const nodes = graph?.nodes || [];
|
||||
const texts = [];
|
||||
|
||||
for (const node of nodes) {
|
||||
if (!resolvedIds.has(node.id)) continue;
|
||||
|
||||
const label = normalise(node.label);
|
||||
const desc = normalise(node.description);
|
||||
|
||||
if (label.length > 0) texts.push({ nodeId: node.id, kind: "label", text: label });
|
||||
if (desc.length > 0) texts.push({ nodeId: node.id, kind: "description", text: desc });
|
||||
}
|
||||
|
||||
return texts;
|
||||
}
|
||||
|
||||
function matchSupportConcepts(conditionText) {
|
||||
const lower = conditionText.toLowerCase();
|
||||
const cats = [];
|
||||
|
||||
for (const [name, group] of Object.entries(CONDITION_GROUPS)) {
|
||||
if (group.support.some((kw) => lower.includes(kw))) {
|
||||
cats.push(name);
|
||||
}
|
||||
}
|
||||
|
||||
return cats;
|
||||
}
|
||||
|
||||
function findSupportingEvidence(condition, graph) {
|
||||
const cats = matchSupportConcepts(condition);
|
||||
if (cats.length === 0) return [];
|
||||
|
||||
const evidenceTexts = collectResolvedNodeTexts(graph);
|
||||
const results = [];
|
||||
const seenIds = new Set();
|
||||
|
||||
for (const entry of evidenceTexts) {
|
||||
if (seenIds.has(entry.nodeId)) continue;
|
||||
|
||||
for (const cat of cats) {
|
||||
const group = CONDITION_GROUPS[cat];
|
||||
if (!group?.support) continue;
|
||||
|
||||
for (const kw of group.support) {
|
||||
if (entry.text.includes(kw)) {
|
||||
results.push(entry.nodeId);
|
||||
seenIds.add(entry.nodeId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [...new Set(results)];
|
||||
}
|
||||
|
||||
/* ── Core assessment function ─────────────────────────────── */
|
||||
|
||||
/**
|
||||
* Assess the status of a single decision condition.
|
||||
*
|
||||
* @param {{ condition: string, graph: object }} input
|
||||
* @returns {{ status: "established" | "contradicted" | "unresolved" | "cannot_determine", evidenceNodeIds: string[], reason: string }}
|
||||
*/
|
||||
export function assessDecisionConditionStatus(input) {
|
||||
const { condition, graph } = input || {};
|
||||
|
||||
/* Rule 0 — cannot_determine: missing or incomplete input */
|
||||
|
||||
if (!condition || typeof condition !== "string" || normalise(condition).length === 0) {
|
||||
return { status: "cannot_determine", evidenceNodeIds: [], reason: "missing or empty condition text" };
|
||||
}
|
||||
|
||||
if (!graph || !Array.isArray(graph.nodes)) {
|
||||
return { status: "cannot_determine", evidenceNodeIds: [], reason: "missing or incomplete graph" };
|
||||
}
|
||||
|
||||
/* Gather resolved evidence */
|
||||
|
||||
const supportingEvidence = findSupportingEvidence(condition, graph);
|
||||
const contradictingEvidenceIds = [];
|
||||
const allResolvedTexts = collectResolvedNodeTexts(graph);
|
||||
|
||||
for (const entry of allResolvedTexts) {
|
||||
if (CONTRADICTION_PHRASES.some((phrase) => entry.text.includes(phrase))) {
|
||||
contradictingEvidenceIds.push(entry.nodeId);
|
||||
}
|
||||
}
|
||||
|
||||
const evidenceNodeIds = [...new Set([...supportingEvidence, ...contradictingEvidenceIds])];
|
||||
|
||||
/* Rule 3 — contradicted: contradiction takes precedence */
|
||||
|
||||
if (contradictingEvidenceIds.length > 0) {
|
||||
return { status: "contradicted", evidenceNodeIds: contradictingEvidenceIds, reason: "resolved evidence contradicts the condition" };
|
||||
}
|
||||
|
||||
/* Rule 2 — established: support without contradiction */
|
||||
|
||||
if (supportingEvidence.length > 0) {
|
||||
return { status: "established", evidenceNodeIds: supportingEvidence, reason: "resolved evidence supports the condition" };
|
||||
}
|
||||
|
||||
/* Rule 4 — unresolved: condition is relevant but no resolved evidence found */
|
||||
|
||||
return { status: "unresolved", evidenceNodeIds: [], reason: "condition is relevant but no resolved evidence establishes or contradicts it" };
|
||||
}
|
||||
Reference in New Issue
Block a user