{"version":3,"file":"approval.d.ts","sourceRoot":"","sources":["../../../src/core/mission/approval.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACX,gBAAgB,EAChB,gBAAgB,EAChB,mBAAmB,EACnB,oBAAoB,EACpB,sBAAsB,EACtB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,gBAAgB,CAAC;IACvB,4EAA4E;IAC5E,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI,sBAAsB,CAAC,CAAC,CAAC,CAAC;AAE1D;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,YAAY,GAAG,cAAc,CAAC,gBAAgB,CAAC,CAiCtF;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,gBAAgB,EAAE,QAAQ,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAM1G;AAMD;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACrC,IAAI,EAAE,mBAAmB,EACzB,iBAAiB,EAAE,MAAM,GAAG,SAAS,EACrC,KAAK,SAAa,GAChB,cAAc,CAAC,oBAAoB,CAAC,CAwBtC","sourcesContent":["/**\n * Durable Mission Graph — human approval gates & external blockers (2.0.0).\n *\n * Human approval nodes cannot be auto-approved. A decision is only valid when\n * granted by a verified principal listed on the gate, for the gate's scope,\n * before its expiry. External blockers require concrete evidence to be marked\n * satisfied — satisfaction cannot be fabricated.\n */\n\nimport type {\n\tApprovalDecision,\n\tApprovalGateSpec,\n\tExternalBlockerSpec,\n\tExternalBlockerState,\n\tMissionOperationResult,\n} from \"./types.js\";\n\nexport interface ApproveInput {\n\tgate: ApprovalGateSpec;\n\t/** Objective attempting the approval (for no-self-approval scope check). */\n\tobjectiveId: string;\n\tprincipal: string;\n\tapproved: boolean;\n\tnowMs: number;\n\treason?: string;\n}\n\nexport type ValidateResult<T> = MissionOperationResult<T>;\n\n/**\n * Evaluate an approval decision request against a gate.\n *\n * Rejections:\n *  - SELF_APPROVAL when the objective approves its own gate\n *  - APPROVAL_NOT_GRANTED when the principal is not required/listed\n *  - APPROVAL_EXPIRED when ttlMs is set and nowMs exceeds expiry window\n */\nexport function evaluateApproval(input: ApproveInput): ValidateResult<ApprovalDecision> {\n\tconst { gate, objectiveId, principal, approved, nowMs, reason } = input;\n\n\t// An objective can never approve its own gate (even if listed).\n\tif (gate.requiredPrincipals.includes(objectiveId)) {\n\t\treturn { ok: false, code: \"SELF_APPROVAL\", error: `objective '${objectiveId}' cannot approve its own gate` };\n\t}\n\n\tif (!gate.requiredPrincipals.includes(principal)) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tcode: \"APPROVAL_NOT_GRANTED\",\n\t\t\terror: `principal '${principal}' is not authorized for gate '${gate.id}'`,\n\t\t};\n\t}\n\n\tlet expiresAtMs: number | undefined;\n\tif (gate.ttlMs !== undefined && approved) {\n\t\t// An approval is valid for `ttlMs` from the moment it is recorded.\n\t\texpiresAtMs = nowMs + gate.ttlMs;\n\t}\n\n\treturn {\n\t\tok: true,\n\t\tvalue: {\n\t\t\tgateId: gate.id,\n\t\t\tapproved,\n\t\t\tprincipal,\n\t\t\texpiresAtMs,\n\t\t\treason,\n\t\t\trecordedAtMs: nowMs,\n\t\t},\n\t};\n}\n\n/**\n * Determine whether a previously recorded approval decision is still valid now:\n * it must be approved, granted by a listed principal, and not expired.\n */\nexport function isApprovalValid(gate: ApprovalGateSpec, decision: ApprovalDecision, nowMs: number): boolean {\n\tif (!decision.approved) return false;\n\tif (!gate.requiredPrincipals.includes(decision.principal)) return false;\n\tif (decision.expiresAtMs !== undefined && nowMs > decision.expiresAtMs) return false;\n\tif (gate.ttlMs !== undefined && decision.recordedAtMs + gate.ttlMs < nowMs) return false;\n\treturn true;\n}\n\n// =============================================================================\n// External blockers\n// =============================================================================\n\n/**\n * Mark an external blocker satisfied given evidence.\n *\n * Rejections:\n *  - BLOCKER_WITHOUT_EVIDENCE when no evidence reference is provided\n *  - BLOCKER_UNSATISFIED when the evidence reference is not in `satisfiedOn`\n *\n * Satisfaction can only come from a concrete evidence reference listed on the\n * spec — it cannot be fabricated.\n */\nexport function satisfyExternalBlocker(\n\tspec: ExternalBlockerSpec,\n\tevidenceReference: string | undefined,\n\tnowMs = Date.now(),\n): ValidateResult<ExternalBlockerState> {\n\tif (spec.evidenceRequired && !evidenceReference) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tcode: \"BLOCKER_WITHOUT_EVIDENCE\",\n\t\t\terror: `external blocker '${spec.id}' requires evidence`,\n\t\t};\n\t}\n\tif (!spec.satisfiedOn.includes(evidenceReference ?? \"\")) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tcode: \"BLOCKER_UNSATISFIED\",\n\t\t\terror: `evidence '${evidenceReference}' does not satisfy blocker '${spec.id}'`,\n\t\t};\n\t}\n\treturn {\n\t\tok: true,\n\t\tvalue: {\n\t\t\tspecId: spec.id,\n\t\t\tsatisfied: true,\n\t\t\tevidenceReference,\n\t\t\tsatisfiedAtMs: nowMs,\n\t\t},\n\t};\n}\n"]}