/** * escalation-engine — pure SLA evaluation and escalation decision engine. * * No I/O, no framework deps. Safe to import in any context (browser, workers, * Node, edge). * * Core API: * evaluateEscalation(item, policy, now) → EscalationDecision * * Properties: * - Idempotent: re-evaluating an already-escalated or terminal item is a no-op. * - Timeout-vs-not: only triggers if the SLA deadline has passed. * - Policy-driven routing: the Vantage ladder IC→Team→Business→Studio. * - Pure: no side effects — the caller is responsible for applying the decision. */ import type { LifecycleItem, LifecyclePriority } from './request-lifecycle.js'; /** * A lightweight reference to an escalation assignee. * * Matches management.studio's PersonRef shape — a pointer into the * id.org.ai identity graph (opaque string + enough display metadata * for audit logs and UI rendering). */ export interface AssigneeRef { /** Unique assignee identifier (personId / team ID / role ID) */ assigneeId: string; /** Display name for audit log legibility */ name: string; /** * Vantage role kind of this escalation target. * Drives routing verification and display labels. */ roleKind: 'ic' | 'team' | 'business' | 'studio'; } /** * One tier in an escalation ladder. * * `afterMs` is the cumulative milliseconds past `slaDeadline` after which * this level activates. Level 0 fires immediately on breach (afterMs = 0). */ export interface EscalationLevel { /** Who receives the item at this escalation level */ assignee: AssigneeRef; /** * Cumulative milliseconds past `slaDeadline` before this level fires. * 0 = fires immediately on SLA breach; positive = additional grace period. */ afterMs: number; } /** * An EscalationPolicy defines when and how a past-SLA item is routed. * * Extends / replaces the package's existing EscalationPolicy from types.ts * with the management.studio-proven AssigneeRef shape instead of bare strings. * * The engine evaluates `conditions` to decide whether to escalate; * `escalationPath[0]` is the first escalation target on SLA breach. */ export interface EscalationPolicy { /** Unique identifier for the policy */ id: string; /** Human-readable name */ name: string; /** * Conditions that trigger escalation. * The engine checks `timeout` (SLA breach) as the primary condition. */ conditions: { /** * Escalate if the item is still active after this many milliseconds * past its `slaDeadline`. 0 = escalate immediately on breach. */ timeout?: number; /** * Only escalate items at or above this priority level. * Undefined = escalate all priorities. */ minPriority?: LifecyclePriority; }; /** * Ordered list of escalation levels. * * Level 0 fires on the first SLA breach (afterMs = 0 by convention). * Subsequent levels define additional grace periods for further escalation. * * The engine applies the highest level whose `afterMs` threshold has been * reached given the current `now` vs `slaDeadline` delta. */ escalationPath: EscalationLevel[]; } /** * The decision returned by evaluateEscalation. * * When `escalate` is true, `nextAssignee` will be populated and `reason` * will contain a human-readable explanation. * * When `escalate` is false, `reason` explains why escalation was skipped * (within-SLA, already terminal, no policy, priority below threshold, etc.). */ export interface EscalationDecision { /** Whether the item should be escalated */ escalate: boolean; /** The recommended next assignee (populated when escalate=true) */ nextAssignee?: AssigneeRef; /** Human-readable reason for the decision */ reason?: string; } /** * evaluateEscalation — pure escalation decision function. * * Given a lifecycle item, an escalation policy, and the current time, * returns an EscalationDecision: * { escalate: true, nextAssignee, reason } → caller should escalate * { escalate: false, reason } → no action needed * * Idempotency contract: * - Already-terminal items (timeout, escalated, completed, cancelled, * released) always return escalate=false. * - Running over the same active item before SLA breach returns false. * - Running over the same active item after SLA breach returns true (once); * after the caller applies the timeout transition the item becomes * terminal and subsequent calls return false. * * The function applies the highest-level escalation path entry whose * `afterMs` threshold has been met: * overdueMsFromSla = now - slaDeadline * highest level where level.afterMs <= overdueMsFromSla * * This enables multi-hop escalation ladders where level 1 fires immediately * and level 2 fires after another N hours of silence. * * @param item - The lifecycle item to evaluate * @param policy - The escalation policy to apply * @param now - Current time (injectable for deterministic tests) */ export declare function evaluateEscalation(item: LifecycleItem, policy: EscalationPolicy, now: Date): EscalationDecision; /** * buildVantageLadder — build the standard IC→Team→Business→Studio escalation * policy for a given IC assignee. * * The Vantage hierarchy (per CONTEXT.md): * IC (widest operational view) → Team manager → Business operator → Studio operator * * This factory produces a single-hop policy (escalationPath[0] only) for * the given assignee → nextAssignee pair. Chain multiple policies (one per * level) or supply a multi-step escalationPath for multi-hop escalation. * * @param id - Policy ID (must be unique in the policy registry) * @param name - Human-readable policy name * @param nextAssignee - The AssigneeRef to escalate to on SLA breach * @param opts - Optional overrides (gracePeriodMs, minPriority) */ export declare function buildVantageLadder(id: string, name: string, nextAssignee: AssigneeRef, opts?: { /** Grace period in ms past the SLA before escalation fires. Default 0. */ gracePeriodMs?: number; /** Only escalate items at or above this priority. Default: all. */ minPriority?: LifecyclePriority; }): EscalationPolicy; /** Fixture: IC-tier assignee (Alex Rivera) */ export declare const FIXTURE_IC: AssigneeRef; /** Fixture: Team-tier assignee (Sam Okafor) */ export declare const FIXTURE_TEAM: AssigneeRef; /** Fixture: Business-tier assignee (Jordan Chen) */ export declare const FIXTURE_BUSINESS: AssigneeRef; /** Fixture: Studio-tier assignee (Morgan Park) */ export declare const FIXTURE_STUDIO: AssigneeRef; /** * FIXTURE_POLICIES — the default Vantage escalation map. * * Keyed by `assigneeId` so callers can look up a policy for the item's * current assignee: * * ```ts * const policy = FIXTURE_POLICIES[item.assignee] * if (!policy) { ... } // Studio operator (no further escalation) * ``` */ export declare const FIXTURE_POLICIES: Record; /** * getFixturePolicy — look up the default Vantage escalation policy for an * assignee ID. * * Returns `undefined` for the Studio operator (Morgan) or any unknown * assignee — caller should treat undefined as "no escalation available". */ export declare function getFixturePolicy(assigneeId: string): EscalationPolicy | undefined; /** * batchEvaluate — evaluate escalation for a list of items. * * For each item, looks up the policy (via the policyLookup function), * calls evaluateEscalation, and collects the decisions. * * Returns a flat list of { item, decision } pairs for ALL items — callers * filter on `decision.escalate` to find items that need action. * * @param items - Items to evaluate * @param policyLookup - Function mapping assigneeId → EscalationPolicy | undefined * @param now - Current time */ export declare function batchEvaluate(items: LifecycleItem[], policyLookup: (assigneeId: string) => EscalationPolicy | undefined, now: Date): Array<{ item: LifecycleItem; policy: EscalationPolicy | undefined; decision: EscalationDecision; }>; /** * buildEscalatedItem — build a new LifecycleItem for the escalation target. * * Copies the original item's content but: * - Assigns a new ID (prefixed with 'esc-' + original ID + timestamp) * - Sets status to 'pending' (target needs to claim it) * - Reassigns assignee to the next assignee * - Clears claimer info (target hasn't claimed it yet) * - Sets a fresh SLA deadline (preserves original SLA duration) * - Embeds the original item's ID in cascade.functionId for traceability * - Preserves teamId/businessId/studioId (work still belongs to same scope) * - Prepends "[Escalated] " to the title so the target sees context * * This is a pure helper — the caller is responsible for persisting the result. */ export declare function buildEscalatedItem(original: LifecycleItem, nextAssignee: AssigneeRef, now: Date): LifecycleItem; //# sourceMappingURL=escalation-engine.d.ts.map