{"version":3,"file":"shadow.d.ts","sourceRoot":"","sources":["../../../src/core/routing/shadow.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,qBAAqB,EAAE,0BAA0B,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEpG,MAAM,WAAW,kBAAkB;IAClC,kBAAkB,EAAE,qBAAqB,CAAC;IAC1C,cAAc,EAAE,MAAM,CAAC;IACvB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC7B,kBAAkB,EAAE,0BAA0B,EAC9C,iBAAiB,EAAE,MAAM,GAAG,SAAS,EACrC,cAAc,EAAE,MAAM,EACtB,mBAAmB,EAAE,MAAM,EAC3B,qBAAqB,EAAE,MAAM,GAAG,SAAS,EACzC,KAAK,EAAE,MAAM,EACb,WAAW,GAAE,MAAM,EAAO,GACxB,cAAc,CAgBhB;AAED,uEAAuE;AACvE,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,cAAc,GAAG,cAAc,CAgB3E","sourcesContent":["/**\n * Zero-effect shadow routing.\n *\n * A shadow policy evaluates the same task context and records what it WOULD\n * have selected, but never executes, never calls a provider, never mutates the\n * workspace, and never affects the current run. Shadow decisions are durable\n * and can later be compared counterfactually.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport { appendEvent, writeShadowDecision } from \"./store.js\";\nimport type { OrchestrationDecision, OrchestrationFeatureVector, ShadowDecision } from \"./types.js\";\n\nexport interface ShadowRoutingInput {\n\tproductionDecision: OrchestrationDecision;\n\tshadowPolicyId: string;\n\tshadowPolicyVersion: number;\n\tshadowCandidateId?: string;\n\treasonCodes?: string[];\n\trunId?: string;\n}\n\n/**\n * Produce a shadow decision. This function is pure with respect to effects:\n * it only records the shadow record and an event; it performs no execution.\n */\nexport function shadowEvaluate(\n\tproductionFeatures: OrchestrationFeatureVector,\n\tshadowCandidateId: string | undefined,\n\tshadowPolicyId: string,\n\tshadowPolicyVersion: number,\n\tproductionCandidateId: string | undefined,\n\trunId: string,\n\treasonCodes: string[] = [],\n): ShadowDecision {\n\tconst shadow: ShadowDecision = {\n\t\tshadowId: randomUUID(),\n\t\tproductionDecisionId: \"\",\n\t\trunId,\n\t\ttaskFingerprint: productionFeatures.featureHash,\n\t\tfeatures: productionFeatures,\n\t\tshadowPolicyId,\n\t\tshadowPolicyVersion,\n\t\tproductionCandidateId,\n\t\tshadowCandidateId,\n\t\twouldSelectDifferent: Boolean(shadowCandidateId) && shadowCandidateId !== productionCandidateId,\n\t\treasonCodes,\n\t\trecordedAt: new Date().toISOString(),\n\t};\n\treturn shadow;\n}\n\n/** Record a shadow decision durably (still zero execution effects). */\nexport function recordShadowDecision(shadow: ShadowDecision): ShadowDecision {\n\twriteShadowDecision(shadow);\n\tappendEvent({\n\t\ttype: \"ORCHESTRATION_SHADOW_DECISION\",\n\t\trunId: shadow.runId,\n\t\tdecisionId: shadow.productionDecisionId || undefined,\n\t\tpolicyId: shadow.shadowPolicyId,\n\t\tpolicyVersion: shadow.shadowPolicyVersion,\n\t\tcandidateId: shadow.shadowCandidateId,\n\t\tevidenceIds: [],\n\t\tpayload: {\n\t\t\twouldSelectDifferent: shadow.wouldSelectDifferent,\n\t\t\tshadowId: shadow.shadowId,\n\t\t},\n\t});\n\treturn shadow;\n}\n"]}