import { ConfidenceLevel, SituationKind, SituationRelationship, SituationStatus, } from "./schema.js"; const DEFAULT_PROMPT_VERSION = "v0.4"; function formatEnumValues(values) { return Object.values(values).join(" | "); } function formatGraph(graph) { return JSON.stringify(graph, null, 2); } function formatExampleAnswerBlock() { return [ "Example answer the model must be able to handle without hard-coding output:", '"The complaint rate fell from 2.0 complaints per 100 units to 1.9 complaints per 100 units."', "This may justify resolving a rate-related unknown or updating a metric node, but only if the current graph and answer support that proposal.", ].join("\n"); } export function buildGraphUpdatePrompt({ situationGraph, previousQuestion, answer, promptVersion = DEFAULT_PROMPT_VERSION, }) { const nodeKinds = formatEnumValues(SituationKind); const nodeStatuses = formatEnumValues(SituationStatus); const edgeRelationships = formatEnumValues(SituationRelationship); const confidenceLevels = formatEnumValues(ConfidenceLevel); return `You are proposing a graph update for Confidence Engine ${promptVersion}. Return exactly one JSON object matching the GraphUpdate contract. Return JSON only. Do not include markdown, explanation, or any text before or after the JSON object. ## Current Situation Graph ${formatGraph(situationGraph)} ## Previous Selected Question ${previousQuestion} ## User Answer ${answer} ## Allowed Node Kinds ${nodeKinds} ## Allowed Node Statuses ${nodeStatuses} ## Allowed Edge Relationships ${edgeRelationships} ## Allowed Confidence Values ${confidenceLevels} ## Required JSON Field Names The JSON object must contain exactly these top-level fields: - addedNodes - updatedNodes - addedEdges - removedEdgeIds - resolvedUnknownNodeIds - affectedNodeIds - selectedQuestion - answerMeaning ## Required Shapes - addedNodes: array of nodes using these exact keys: id, label, description, kind, status, confidence, value, unit, evidenceIds, dependsOn, affects, parentId, childIds - updatedNodes: array of node updates using these exact keys: nodeId, previousStatus, newStatus, previousValue, newValue, reason - addedEdges: array of edges using these exact keys: id, fromNodeId, toNodeId, relationship, confidence, description - removedEdgeIds: array of strings - resolvedUnknownNodeIds: array of strings - affectedNodeIds: array of strings - selectedQuestion: either null or an object using these exact keys: nodeId, question, reason - answerMeaning: either null or an object using these exact keys: userSupportedMeaning, possibleInference, supportCategory, resolutionGuidance ## Proposal Rules 1. Propose changes only. Never return a replacement graph. 2. Preserve unrelated nodes and edges by omitting them from the proposal. 3. Reference existing node IDs when updating an existing concept. 4. Use addedNodes only for genuinely new concepts. 5. Resolve the answered unknown first when the answer supports it. 6. Then inspect the answer for newly introduced consequential uncertainty. 7. Add new unknown nodes only when the answer introduces a new decision, claim, object, measure, dependency, or unresolved term directly relevant to the case. 8. Add at most 3 new unknown nodes. 9. Every new unknown must be directly traceable to the user's answer and its description must state why that uncertainty matters. 9a. In the description of every new unknown, explicitly include a short why-it-matters clause using wording such as because, so that, needed to decide, or matters because. 10. Do not add broad generic discovery questions. 11. Do not add duplicate unknowns. 12. Do not expand unrelated branches. 13. Propagate only through explicit dependencies or relationships already present in the graph, except for the minimal new edges needed to connect validated new unknowns to the relevant answer-derived decision or context node. 13a. For every new unknown node, include at least one added edge that connects it to an existing updated/resolved node or to a newly added non-unknown node introduced from the answer. 14. Do not invent evidence. 15. Do not create unsupported causal edges. 16. If consequential unresolved unknowns exist, selectedQuestion may identify one valid candidate unknown, but the engine will deterministically choose final priority after validation. 17. selectedQuestion.nodeId must reference an unresolved unknown node that exists either already in the graph or in addedNodes. 18. selectedQuestion.question must be one narrow non-compound question about that one unknown. 19. Do not prioritise downstream implementation, pricing, optimisation, or speculative branches ahead of prerequisite definitions, actors, success criteria, constraints, measures, or terminology. 20. Return selectedQuestion as null only when no consequential unresolved unknown remains. 21. Use empty arrays when there are no changes in a category. 22. Never return null array entries. 23. Never use unknown enum values. 24. Do not change existing IDs. 25. Do not replace the whole graph, and do not restate unchanged graph content inside the proposal. 26. answerMeaning.userSupportedMeaning must state only what the user's answer directly supports. 27. Put any stronger interpretation in answerMeaning.possibleInference, not in userSupportedMeaning. 28. If the answer is only a relative priority statement, use supportCategory=relative_priority_only and resolutionGuidance=must_remain_unresolved. 29. If the answer is conditional or qualified, preserve that qualification explicitly in userSupportedMeaning. 30. If the answer says the user is unsure or does not resolve the distinction, use supportCategory=uncertain and resolutionGuidance=must_remain_unresolved. 31. If the answer explicitly states a hard constraint, use supportCategory=explicit_hard_constraint and resolutionGuidance=must_resolve. ## Additional Guidance - If the answer only clarifies an existing unknown, prefer updatedNodes and resolvedUnknownNodeIds over creating duplicate nodes. - When an answer resolves an existing unknown, include that existing node ID in resolvedUnknownNodeIds and update that node rather than creating only a parallel observation. - If the answer creates a more specific decision situation, add the smallest set of new nodes and edges needed to represent that situation and only its most consequential unknowns. - If you add a new unknown, do not leave it floating: connect it with an added edge to the relevant decision/context node created or updated from the answer. - If you add a new unknown, its description must do two jobs in one sentence: what is unknown, and why resolving it matters for the case. - Treat selectedQuestion as a candidate only; the engine will apply deterministic information-value scoring after validation. - If the answer does not justify a change, return empty arrays for every category. - Use answerMeaning to preserve the answer's direct meaning even when the graph change remains unresolved. ## Example Constraint Reminder ${formatExampleAnswerBlock()} ## Output Contract Reminder Return one JSON object only, with exact field names and exact enum values. Never include a full graph. Never include any field other than the contract fields above. `; } export const buildUpdatePrompt = buildGraphUpdatePrompt;