{"version":3,"file":"counterfactual.d.ts","sourceRoot":"","sources":["../../../src/core/routing/counterfactual.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD,MAAM,MAAM,kBAAkB,GAAG,eAAe,GAAG,mBAAmB,GAAG,oBAAoB,GAAG,aAAa,CAAC;AAE9G,MAAM,WAAW,sBAAsB;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,IAAI,EAAE,kBAAkB,CAAC;IACzB,SAAS,EAAE,QAAQ,GAAG,eAAe,GAAG,qBAAqB,GAAG,MAAM,CAAC;IACvE,0BAA0B,EAAE,OAAO,CAAC;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,kBAAkB,CAAC,EAAE,iBAAiB,CAAC;IACvC,sBAAsB,CAAC,EAAE,iBAAiB,CAAC;IAC3C,4CAA4C;IAC5C,UAAU,EAAE;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC;CAC1F;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,mBAAmB,GAAG,sBAAsB,CAwCzF","sourcesContent":["/**\n * Counterfactual evaluation.\n *\n * Estimates whether another candidate may have performed better using\n * evaluation scenarios, replays, or comparable historical runs. Results are\n * clearly labeled as estimates, carry explicit uncertainty, identify the\n * off-policy estimator, and never retroactively change completed-run authority.\n */\n\nimport type { CandidateEvidence } from \"./types.js\";\n\nexport type CounterfactualMode = \"direct_paired\" | \"replay_compatible\" | \"matched_historical\" | \"unsupported\";\n\nexport interface CounterfactualEstimate {\n\tdecisionId: string;\n\tproductionCandidateId?: string;\n\tcounterfactualCandidateId?: string;\n\tmode: CounterfactualMode;\n\testimator: \"direct\" | \"doubly_robust\" | \"importance_sampling\" | \"none\";\n\testimatedWouldHaveImproved: boolean;\n\teffectSize: number;\n\tuncertainty: number;\n\tsupported: boolean;\n\treasonCodes: string[];\n}\n\nexport interface CounterfactualInput {\n\tdecisionId: string;\n\tproductionCandidateId?: string;\n\tcounterfactualCandidateId?: string;\n\tproductionEvidence?: CandidateEvidence;\n\tcounterfactualEvidence?: CandidateEvidence;\n\t/** Environment/task compatibility flags. */\n\tcompatible: { task: boolean; environment: boolean; scenario: boolean; identity: boolean };\n}\n\n/**\n * Produce a counterfactual estimate. Only produces a supported numeric estimate\n * when evidence for both candidates exists and identities are compatible.\n * Otherwise it reports an explicit \"unsupported\" mode with no causal claim.\n */\nexport function evaluateCounterfactual(input: CounterfactualInput): CounterfactualEstimate {\n\tconst compat = input.compatible;\n\tconst identCompatible = compat.task && compat.environment && compat.scenario && compat.identity;\n\n\tif (!identCompatible || !input.productionEvidence || !input.counterfactualEvidence) {\n\t\treturn {\n\t\t\tdecisionId: input.decisionId,\n\t\t\tproductionCandidateId: input.productionCandidateId,\n\t\t\tcounterfactualCandidateId: input.counterfactualCandidateId,\n\t\t\tmode: identCompatible ? \"matched_historical\" : \"unsupported\",\n\t\t\testimator: \"none\",\n\t\t\testimatedWouldHaveImproved: false,\n\t\t\teffectSize: 0,\n\t\t\tuncertainty: 1,\n\t\t\tsupported: false,\n\t\t\treasonCodes: [\"incompatible_identity\", \"missing_evidence\"],\n\t\t};\n\t}\n\n\tconst prod = input.productionEvidence;\n\tconst cf = input.counterfactualEvidence;\n\n\tconst prodScore =\n\t\t(prod.correctnessRate ?? 0) * 0.5 + (prod.reliabilityRate ?? 0) * 0.3 + (prod.safetyRate ?? 0) * 0.2;\n\tconst cfScore = (cf.correctnessRate ?? 0) * 0.5 + (cf.reliabilityRate ?? 0) * 0.3 + (cf.safetyRate ?? 0) * 0.2;\n\tconst effectSize = cfScore - prodScore;\n\tconst uncertainty = 0.5 + Math.max(0, 1 - Math.min(1, Math.min(prod.sampleCount, cf.sampleCount) / 20)) * 0.4;\n\n\treturn {\n\t\tdecisionId: input.decisionId,\n\t\tproductionCandidateId: input.productionCandidateId,\n\t\tcounterfactualCandidateId: input.counterfactualCandidateId,\n\t\tmode: \"matched_historical\",\n\t\testimator: \"direct\",\n\t\testimatedWouldHaveImproved: effectSize > 0,\n\t\teffectSize: Number(effectSize.toFixed(4)),\n\t\tuncertainty: Number(uncertainty.toFixed(4)),\n\t\tsupported: true,\n\t\treasonCodes: [\"direct_paired_estimate\", \"uncertainty_explicit\"],\n\t};\n}\n"]}