{"version":3,"file":"consistency.d.ts","sourceRoot":"","sources":["../../../src/core/web-research/consistency.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAE5F;;;;;;;;;GASG;AAEH,MAAM,WAAW,YAAY;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;CACnC;AAED,MAAM,WAAW,oBAAoB;IACpC,qBAAqB,CAAC,EAAE,YAAY,EAAE,CAAC;IACvC,eAAe,CAAC,EAAE,YAAY,EAAE,CAAC;IACjC,mBAAmB,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAC3C,oBAAoB,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAC7C,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,qBAAa,eAAe;IAC3B;;;OAGG;IACH,2BAA2B,CAAC,KAAK,EAAE,oBAAoB,GAAG,gBAAgB,EAAE,CA+B3E;IAED;;;OAGG;IACH,uBAAuB,CAAC,KAAK,EAAE,oBAAoB,GAAG,gBAAgB,EAAE,CAcvE;IAED;;;OAGG;IACH,6BAA6B,CAAC,KAAK,EAAE,oBAAoB,GAAG,gBAAgB,EAAE,CAiB7E;IAED,GAAG,CAAC,KAAK,EAAE,oBAAoB,GAAG,gBAAgB,EAAE,CAMnD;CACD","sourcesContent":["import type { ConsistencyIssue, NumericVerification, TemporalResolution } from \"./types.js\";\n\n/**\n * Final consistency gate. Detects when prose conclusions contradict the\n * report's own structured facts.\n *\n * Checks:\n * - ranked recommendations against computed metrics\n * - \"best/current/meta\" claims against newer equipment in the same run\n * - conclusions based on historical values when current values are available\n * - claims of superiority that depend on unmodeled mechanics\n */\n\nexport interface RankedOption {\n\tid: string;\n\tlabel: string;\n\tcomputedMetric: number | undefined;\n}\n\nexport interface ConsistencyGateInput {\n\trankedRecommendations?: RankedOption[];\n\tcomputedMetrics?: RankedOption[];\n\ttemporalResolutions?: TemporalResolution[];\n\tnumericVerifications?: NumericVerification[];\n\tcurrentValues?: string[];\n}\n\nexport class ConsistencyGate {\n\t/**\n\t * Raise an issue when a recommendation rank disagrees with the ordering of\n\t * the report's own computed metrics.\n\t */\n\tcheckRecommendationOrdering(input: ConsistencyGateInput): ConsistencyIssue[] {\n\t\tconst issues: ConsistencyIssue[] = [];\n\t\tconst recs = input.rankedRecommendations ?? [];\n\t\tconst metrics = input.computedMetrics ?? [];\n\t\tfor (const rec of recs) {\n\t\t\tconst metric = metrics.find((m) => m.id === rec.id)?.computedMetric;\n\t\t\tif (metric === undefined) {\n\t\t\t\tissues.push({\n\t\t\t\t\tkind: \"unmodeled_mechanics\",\n\t\t\t\t\tseverity: \"warning\",\n\t\t\t\t\tmessage: `Recommendation \"${rec.label}\" has no computed metric.`,\n\t\t\t\t\trecommendation: \"Qualify the recommendation or add a computed metric before ranking.\",\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// Ensure every higher-ranked option has a metric >= this one.\n\t\t\tconst rank = recs.findIndex((r) => r.id === rec.id);\n\t\t\tfor (let i = 0; i < rank; i++) {\n\t\t\t\tconst higher = recs[i];\n\t\t\t\tconst higherMetric = metrics.find((m) => m.id === higher.id)?.computedMetric;\n\t\t\t\tif (higherMetric !== undefined && higherMetric < metric) {\n\t\t\t\t\tissues.push({\n\t\t\t\t\t\tkind: \"rank_contradicts_metric\",\n\t\t\t\t\t\tseverity: \"error\",\n\t\t\t\t\t\tmessage: `\"${higher.label}\" is ranked above \"${rec.label}\" but its computed metric (${higherMetric}) is below (${metric}).`,\n\t\t\t\t\t\trecommendation: \"Re-rank recommendations to match the computed metrics or qualify the claim.\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn issues;\n\t}\n\n\t/**\n\t * Raise an issue when a temporal resolution leaves a contradiction but the\n\t * synthesis still asserts a definite current value.\n\t */\n\tcheckUnresolvedTemporal(input: ConsistencyGateInput): ConsistencyIssue[] {\n\t\tconst issues: ConsistencyIssue[] = [];\n\t\tconst unresolved = (input.temporalResolutions ?? []).filter(\n\t\t\t(t) => t.class === \"contradiction\" || t.class === \"uncertain_current\",\n\t\t);\n\t\tfor (const t of unresolved) {\n\t\t\tissues.push({\n\t\t\t\tkind: \"unresolved_temporal\",\n\t\t\t\tseverity: \"warning\",\n\t\t\t\tmessage: `Value ${String(t.value)} at ${t.sourceUrl} is unresolved (${t.class}).`,\n\t\t\t\trecommendation: \"Do not state a definite current value; present it as a conditional scenario.\",\n\t\t\t});\n\t\t}\n\t\treturn issues;\n\t}\n\n\t/**\n\t * Raise an issue when a \"best/meta\" recommendation is based on a historical\n\t * value while a current value exists.\n\t */\n\tcheckHistoricalRecommendation(input: ConsistencyGateInput): ConsistencyIssue[] {\n\t\tconst issues: ConsistencyIssue[] = [];\n\t\tconst currentValues = input.currentValues ?? [];\n\t\tconst historical = (input.temporalResolutions ?? []).filter(\n\t\t\t(t) => t.class === \"historical\" || t.class === \"superseded\",\n\t\t);\n\t\tfor (const t of historical) {\n\t\t\tif (currentValues.includes(String(t.value))) {\n\t\t\t\tissues.push({\n\t\t\t\t\tkind: \"historical_recommendation\",\n\t\t\t\t\tseverity: \"warning\",\n\t\t\t\t\tmessage: `Recommendation relies on historical value ${String(t.value)}; a current value exists.`,\n\t\t\t\t\trecommendation: \"Base 'best/current/meta' recommendations on current values.\",\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\treturn issues;\n\t}\n\n\trun(input: ConsistencyGateInput): ConsistencyIssue[] {\n\t\treturn [\n\t\t\t...this.checkRecommendationOrdering(input),\n\t\t\t...this.checkUnresolvedTemporal(input),\n\t\t\t...this.checkHistoricalRecommendation(input),\n\t\t];\n\t}\n}\n"]}