Files
confidence-engine/lib/graph/focused-investigation.js
T

141 lines
7.5 KiB
JavaScript

import { formulateQuestion } from "@/lib/graph/question-formulator.js";
const FOCUSED_ANSWER_SCHEMA_FIELDS = [
"targetNodeId",
"observations",
"uncertainties",
"assumptions",
"relationships",
"possibleFollowUpQuestions",
];
const FORBIDDEN_GRAPH_MUTATION_FIELDS = [
"addedNodes",
"updatedNodes",
"removedNodes",
"addedEdges",
"removedEdges",
"resolvedNodeIds",
"activeUnknownNodeId",
"selectedQuestion",
];
/**
* Formulate a question for the explicitly user-selected unresolved node.
*
* Input: { situationGraph, targetNodeId }
* Validates that target exists, is an unknown, and is not resolved.
* Calls formulateQuestion with only factual explicit-user-targeting context.
* Does NOT invoke selectActiveUnknownCandidate, determineGraphBackedQuestion,
* global ranking, or global recommendation.
* Does NOT mutate activeUnknownNodeId, selectedQuestion, SituationGraph, or resolvedNodeIds.
*/
export function formulateQuestionForTarget({ situationGraph, targetNodeId }) {
const nodesById = new Map(situationGraph?.nodes?.map((n) => [n.id, n]) || []);
const targetNode = nodesById.get(targetNodeId);
if (!targetNode) {
return { success: false, error: `Target node ${targetNodeId} not found in graph.` };
}
if (targetNode.kind !== "unknown") {
return { success: false, error: `Target node ${targetNodeId} is not an unknown (kind=${targetNode.kind}).` };
}
if (targetNode.status === "resolved") {
return { success: false, error: `Target node ${targetNodeId} is already resolved.` };
}
// Build explicit user-targeting context — only factual fields from the targeting request.
// No activeUnknownNodeId override, no selectedQuestion mutation, no global selection state.
const context = {
resolvedValues: [],
suppressDecisionSufficiencyConfirmation: false,
_explicitTargetNodeId: targetNodeId,
_explicitTargetLabel: targetNode.label,
_explicitTargetDescription: targetNode.description,
};
const result = formulateQuestion({ node: targetNode, graph: situationGraph, context });
return {
success: true,
targetNodeId,
question: result.question,
strategy: result.strategy ?? null,
reasoningPattern: result.reasoningPattern,
reasoningPatternReason: result.reasoningPatternReason,
reason: result.reason,
questionFamily: result.questionFamily,
selectedQuestionTemplate: result.selectedQuestionTemplate,
allowedQuestionFamilies: result.allowedQuestionFamilies,
rejectedQuestionFamilies: result.rejectedQuestionFamilies,
};
}
/**
* Build a focused answer deconstruction prompt for one explicitly user-chosen investigation.
*/
export function buildFocusedDeconstructPrompt({ targetLabel, targetDescription, centralStatement, question, answer }) {
return `You are performing focused answer deconstruction for one explicitly user-chosen investigation.
Return exactly one JSON object. Return JSON only.
This is NOT a graph update task.
Do NOT output graph mutations.
Do NOT output selection, ranking, ownership, recommendation, confidence, or next-best-question semantics.
Do NOT include any of these fields: addedNodes, updatedNodes, removedNodes, addedEdges, removedEdges, resolvedNodeIds, activeUnknownNodeId, selectedQuestion.
Required top-level fields:
- targetNodeId
- observations
- uncertainties
- assumptions
- relationships
- possibleFollowUpQuestions
Field rules (semantic contract):
- targetNodeId must be included as a string identifying this investigation node
- observations: only meaning directly supported by what the user's answer states. Do not strengthen implications into observations.
- uncertainties: only things the answer explicitly leaves unknown or unclear. Preserve uncertainty at the narrowest scope justified by the answer: when the answer establishes one factor but provides no evidence about what else may matter, keep the remaining uncertainty broad rather than inventing specific additional factors, deficits, causes, requirements, or interventions.
- assumptions: what unstated proposition does the user's answer itself rely upon for it to make sense? Include only when such a proposition is genuinely attributable to the user's reasoning. The boundary is narrow: attribute only propositions that the user's answer would cease to make sense if they were false. Do NOT import plausible interpretations from the wider investigation context, scenario framing, domain relevance, strategic implications, or model-generated analysis into this field — those belong in uncertainties, relationships (where permitted), or possibleFollowUpQuestions. Do NOT connect a factual statement the user makes to a broader capability or constraint concept unless the user explicitly links them. Example: answering "I only have bank account access" to a question about delegation constraints does NOT assume that "delegation feasibility is contingent upon banking access" — it only states a fact about access, and connecting that fact to delegation feasibility is your own scenario-level inference, not a user-held assumption. If the user's answer does not contain or rely upon an identifiable assumption, return assumptions: []. Do NOT require verbatim copying from the user's answer; paraphrasing is allowed only when the reasoning genuinely relies on it.
- relationships: only connections that the user's answer directly establishes between items. Co-mentioned facts do not by themselves create causal, constraint, or dependency relationships. If a relationship is only plausible, omit it rather than assert it.
- possibleFollowUpQuestions: questions that investigate genuinely unresolved areas exposed by this answer. Before formulating each follow-up, check whether the question tests a proposition (e.g., "there is a deficit", "X is required", "intervention Y should happen") against the current epistemic state or assumes it as already established. If an explanation, deficit, dependency, cause, intervention, recommendation, or solution has not been established by prior evidence, phrase the question so it tests whether that proposition is true rather than assuming it — verify the unresolved fact before seeking remedy. Prefer questions that identify what remains unknown, distinguish competing explanations, test whether a suspected factor actually matters, clarify scope, or identify what evidence would change the investigation. Do not jump to implementation details unless the answer has already established that intervention as the relevant next issue.
- cross-field ownership: preserve who or what owns each proposition. When a statement expresses the user's comfort, willingness, threshold, belief, uncertainty, preference, or judgement, keep it attached to that stance — do not elevate it into an objective requirement, capability fact, or situational constraint.
Focused case context:
- target label: ${targetLabel}
- target description: ${targetDescription}
- central case statement: ${centralStatement}
Question:
${question}
Answer:
${answer}`;
}
/**
* Validate that a focused-answer deconstruction result contains only the expected fields.
* Returns an array of errors (empty = valid).
*/
export function validateFocusedDeconstructSchema(result) {
const errors = [];
for (const field of FOCUSED_ANSWER_SCHEMA_FIELDS) {
if (!(field in result)) {
errors.push(`Missing required field: ${field}`);
}
}
for (const field of FORBIDDEN_GRAPH_MUTATION_FIELDS) {
if (field in result) {
errors.push(`Forbidden graph-mutation field present: ${field}`);
}
}
return errors;
}
export { FOCUSED_ANSWER_SCHEMA_FIELDS, FORBIDDEN_GRAPH_MUTATION_FIELDS };