{"version":3,"file":"review-gate.mjs","names":[],"sources":["../../../../../../../ai/src/skills/review-gate.ts"],"sourcesContent":["import type {\n  SkillAnalyticsEvent,\n  SkillReviewGate,\n} from \"./contracts/skills-config.type\";\nimport type { SkillRecord } from \"./contracts/skill-record.type\";\n\n/** Outcome of running a candidate through the review gate. */\nexport type ReviewOutcome =\n  | { promoted: true; record: SkillRecord; reason?: string }\n  | { promoted: false; reason?: string };\n\n/**\n * Run a candidate through the **default-DENY** review gate (Phase 2).\n *\n * The gate's `approve(candidate)` decides: only `{ approve: true }`\n * promotes the candidate to a new audited version via `gate.store.promote`.\n * Everything else — `{ approve: false }`, a malformed result, or a THROW\n * (fail-closed) — leaves the candidate inert and emits a `denied` event.\n * On approval, a `promoted` event fires with the new version.\n *\n * Analytics errors are swallowed by the supplied sink wrapper; this runner\n * never throws — a gate that throws is simply treated as a denial.\n *\n * @example\n * const outcome = await runReviewGate(candidate, gate, emit);\n * if (outcome.promoted) console.log(\"now at v\" + outcome.record.version);\n */\nexport async function runReviewGate(\n  candidate: SkillRecord,\n  gate: SkillReviewGate,\n  emit?: (event: SkillAnalyticsEvent) => void,\n): Promise<ReviewOutcome> {\n  let verdict: { approve: boolean; reason?: string };\n\n  try {\n    verdict = await gate.approve(candidate);\n  } catch (error) {\n    // Fail-closed: a throwing gate is a denial, never an accidental promotion.\n    const reason = error instanceof Error ? error.message : String(error);\n\n    emit?.({ type: \"denied\", skill: candidate.name, version: candidate.version });\n\n    return { promoted: false, reason };\n  }\n\n  if (!verdict || verdict.approve !== true) {\n    emit?.({ type: \"denied\", skill: candidate.name, version: candidate.version });\n\n    return { promoted: false, reason: verdict?.reason };\n  }\n\n  const record = await gate.store.promote(candidate.name);\n\n  emit?.({ type: \"promoted\", skill: record.name, version: record.version });\n\n  return { promoted: true, record, reason: verdict.reason };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA2BA,eAAsB,cACpB,WACA,MACA,MACwB;CACxB,IAAI;CAEJ,IAAI;EACF,UAAU,MAAM,KAAK,QAAQ,SAAS;CACxC,SAAS,OAAO;EAEd,MAAM,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;EAEpE,OAAO;GAAE,MAAM;GAAU,OAAO,UAAU;GAAM,SAAS,UAAU;EAAQ,CAAC;EAE5E,OAAO;GAAE,UAAU;GAAO;EAAO;CACnC;CAEA,IAAI,CAAC,WAAW,QAAQ,YAAY,MAAM;EACxC,OAAO;GAAE,MAAM;GAAU,OAAO,UAAU;GAAM,SAAS,UAAU;EAAQ,CAAC;EAE5E,OAAO;GAAE,UAAU;GAAO,QAAQ,SAAS;EAAO;CACpD;CAEA,MAAM,SAAS,MAAM,KAAK,MAAM,QAAQ,UAAU,IAAI;CAEtD,OAAO;EAAE,MAAM;EAAY,OAAO,OAAO;EAAM,SAAS,OAAO;CAAQ,CAAC;CAExE,OAAO;EAAE,UAAU;EAAM;EAAQ,QAAQ,QAAQ;CAAO;AAC1D"}