/**
 * Reconcile Rules — Individual reconciliation checks
 *
 * Each rule inspects a specific aspect of system state and returns
 * corrections when discrepancies are found. Rules follow the CEO principle:
 * "All automation is explainable" — every correction records who, what, why.
 *
 * @module services/reconciler/reconcile-rules
 */
import type { WorkItem, Request, TaskClaim, ReconcileCorrection, WakeAction, AgentScoreBreakdown } from '../../types/v2/index.js';
/**
 * Minimal agent info needed by reconcile rules.
 * Abstraction over AgentRegistrationService / StorageService.
 */
export interface AgentHealth {
    sessionName: string;
    status: 'active' | 'started' | 'inactive' | 'suspended' | 'unknown';
    lastSeenAt?: string;
    /** Agent role (developer, qa, team-lead, etc.) — used for skill matching */
    role?: string;
    /** Tags describing agent capabilities (e.g. ['backend', 'typescript', 'rust']) */
    tags?: string[];
    /** Number of WorkItems currently assigned to this agent */
    activeWorkItemCount?: number;
    /** Team ID the agent belongs to */
    teamId?: string;
    /** Member ID within the team */
    memberId?: string;
}
/**
 * Per-WorkItemType timeout overrides for the stuck detector.
 *
 * **Why per-type:** the original single-value timeout (10 min) treated every
 * WI the same — including `delegate`/`review` WIs that, by design, supervise
 * multi-actor work spanning hours. A 10-min ceiling on a TL umbrella will
 * always fire while the TL is still actively orchestrating, falsely marking
 * it `failed` and (via cascade rules) cancelling its children. Workshop
 * dogfood on 2026-05-06 lost 6 P0 child WIs to exactly this race.
 *
 * Defaults:
 *   - `delegate`, `review` — 4h (these supervise long-running coordination)
 *   - `confirm`            — Number.POSITIVE_INFINITY (waits for user; never timeout)
 *   - everything else      — falls back to the caller-provided default
 *
 * Callers may pass a partial override map to tighten or loosen any entry.
 */
export declare const DEFAULT_PER_TYPE_TIMEOUT_MS: Partial<Record<WorkItem['type'], number>>;
/**
 * Detects WorkItems that are 'running' but whose assigned agent is not alive.
 * Returns corrections to transition them to 'blocked' or 'failed'.
 *
 * @param workItems - All running WorkItems
 * @param agentHealthMap - Map of agent session → health info
 * @param timeoutMs - Default running timeout (default: 10 min); per-type overrides
 *                   in {@link DEFAULT_PER_TYPE_TIMEOUT_MS} apply on top of this
 * @param timeoutOverrides - Optional caller-supplied per-type overrides
 * @returns Array of corrections and affected WorkItem IDs
 */
export declare function detectStuckWorkItems(workItems: WorkItem[], agentHealthMap: Map<string, AgentHealth>, timeoutMs?: number, timeoutOverrides?: Partial<Record<WorkItem['type'], number>>): {
    corrections: ReconcileCorrection[];
    stuckIds: string[];
};
/**
 * Detects TaskClaims whose lease has expired and should be released or revoked.
 *
 * @param claims - All active claims
 * @param gracePeriodMs - Grace period duration in ms
 * @returns Claims to mark as expiring and claims to revoke
 */
export declare function detectExpiredClaims(claims: TaskClaim[], gracePeriodMs?: number): {
    corrections: ReconcileCorrection[];
    expiringIds: string[];
    revokedIds: string[];
};
/**
 * Recomputes the correct status for a Request based on its WorkItems' statuses.
 * This is the core "truth recomputation" — events are hints, this is truth.
 *
 * @param request - The Request to reconcile
 * @param workItems - All WorkItems belonging to this Request
 * @returns Correction if status needs updating, null otherwise
 */
export declare function reconcileRequestStatus(request: Request, workItems: WorkItem[]): ReconcileCorrection | null;
/**
 * Detects WorkItems whose parent has been permanently cancelled/failed but
 * are still active. These should be cascade-cancelled.
 *
 * Children of `failed`-but-retryable parents are intentionally **skipped** —
 * see {@link isParentPermanentlyTerminal} for the rationale.
 *
 * @param workItems - All WorkItems to check
 * @param workItemMap - Map of WorkItem ID → WorkItem for parent lookup
 * @returns Corrections for orphan WorkItems
 */
export declare function detectOrphanWorkItems(workItems: WorkItem[], workItemMap: Map<string, WorkItem>): {
    corrections: ReconcileCorrection[];
    orphanIds: string[];
};
/**
 * Detects WorkItems that have exceeded their time-to-live.
 * Default TTL is 24 hours.
 *
 * Picks a state-machine-legal terminal target for each expired WI via
 * {@link pickTTLExpiryTarget}, so the correction never fails with
 * `Invalid status transition`.
 *
 * @param workItems - All non-terminal WorkItems to check
 * @param ttlMs - Maximum age before auto-cancel (default: 24h)
 * @returns Corrections for expired WorkItems
 */
export declare function detectTTLExpiredWorkItems(workItems: WorkItem[], ttlMs?: number): {
    corrections: ReconcileCorrection[];
    expiredIds: string[];
};
/**
 * Detects blocked WorkItems whose assigned agent is now alive,
 * suggesting they can be re-queued for retry.
 *
 * @param workItems - All blocked WorkItems
 * @param agentHealthMap - Map of agent session → health info
 * @returns Corrections to re-queue recoverable WorkItems
 */
export declare function detectRecoverableWorkItems(workItems: WorkItem[], agentHealthMap: Map<string, AgentHealth>): {
    corrections: ReconcileCorrection[];
    recoverableIds: string[];
};
/**
 * Performs deep cascade cancellation: when a WorkItem is cancelled/failed,
 * all descendants (children, grandchildren, etc.) should be cancelled too.
 * This is a recursive version of detectOrphanWorkItems.
 *
 * @param cancelledIds - Set of WorkItem IDs that have been cancelled/failed
 * @param allWorkItems - All WorkItems in the system
 * @returns Corrections for cascaded cancellations
 */
export declare function cascadeCancelChildren(cancelledIds: Set<string>, allWorkItems: WorkItem[]): {
    corrections: ReconcileCorrection[];
    cascadedIds: string[];
};
/**
 * Detects WorkItems that have been in 'queued' status for too long
 * without being picked up. These may indicate assignment problems.
 *
 * **2026-05-16 policy change — DO NOT auto-cancel stale queued WIs.**
 * The earlier behavior emitted `queued → cancelled` corrections whenever
 * a WI sat queued for more than `staleThresholdMs` (default 1h). On
 * 2026-05-16 a real user request (Steve's X-article analysis dispatched
 * to inactive Atlas) was destroyed by this rule: the WI sat queued
 * because the target agent was dead, the 60-min timer expired before
 * orc could re-wake Atlas, and the WI was silently cancelled. Cancelling
 * was *masking* the underlying bugs (wake-gate ordering race, idle
 * agents holding the floor under memory pressure) instead of surfacing
 * them. PR #585's eviction-under-pressure is the right mechanism to
 * actually make progress on stale queued WIs — wake their target rather
 * than kill the work.
 *
 * This function now returns ONLY `staleIds` for observability (so
 * callers can log/escalate the stuck condition); no corrections are
 * emitted, so applying the returned (empty) corrections is a no-op. The
 * `corrections` field is preserved for API compatibility with
 * `runPruningPass` which spreads it into the aggregate list.
 *
 * Historical context preserved:
 *   - F-CYCLE7-3 (2026-05-07) was an earlier fix that switched the
 *     emitted correction from `queued→queued` (invalid transition) to
 *     `queued→cancelled` (valid but destructive). The destructive flip
 *     is what we're now reverting; the transition-table-validity lesson
 *     still applies to other rules that DO emit corrections.
 *   - Canonical state machine (`work-item.types.ts`): `queued` → one of
 *     `{ running, proposed, scheduled, cancelled }`.
 *
 * @param workItems - All WorkItems to check
 * @param staleThresholdMs - How long in queued before considered stale (default: 1h)
 * @returns `{ corrections: [], staleIds }` — observability-only. The
 *   `corrections` array is always empty; iterate `staleIds` for the
 *   list of WIs that have been queued past `staleThresholdMs`.
 */
export declare function detectStaleQueuedWorkItems(workItems: WorkItem[], staleThresholdMs?: number): {
    corrections: ReconcileCorrection[];
    staleIds: string[];
};
/**
 * Result of a pruning pass, aggregating all pruning-related corrections.
 */
export interface PruningResult {
    /** WorkItems cancelled due to TTL expiry */
    ttlExpiredCount: number;
    /** WorkItems cancelled due to orphan cascade */
    orphanCancelledCount: number;
    /** WorkItems cancelled due to deep cascade */
    cascadeCancelledCount: number;
    /**
     * WorkItems still queued past `staleThresholdMs`. Surfaced for
     * observability ONLY — no longer auto-cancelled (policy change
     * 2026-05-16; see {@link detectStaleQueuedWorkItems} banner).
     */
    staleQueuedCount: number;
    /**
     * IDs of the still-queued WIs that exceeded the staleness threshold.
     * Callers (the reconciler service) log these so the stuck condition
     * is visible without destroying the work. Empty when none are stale.
     */
    staleQueuedIds: string[];
    /** Total corrections from pruning */
    totalCorrections: ReconcileCorrection[];
}
/**
 * Runs a complete pruning pass — combines TTL, orphan, cascade, and stale detection.
 * Called as part of the Reconciler's full loop every 5 minutes.
 *
 * @param allWorkItems - All WorkItems in the system
 * @param ttlMs - TTL threshold (default: 24h)
 * @param staleThresholdMs - Stale queue threshold (default: 1h)
 * @returns Aggregated pruning result
 */
export declare function runPruningPass(allWorkItems: WorkItem[], ttlMs?: number, staleThresholdMs?: number): PruningResult;
/** Default threshold for unclaimed task detection (2 minutes). */
export declare const UNCLAIMED_THRESHOLD_MS: number;
/** Maximum number of wake actions per reconcile pass (rate limit). */
export declare const MAX_WAKE_ACTIONS_PER_PASS = 3;
/**
 * Result from the detectUnclaimedTasks rule.
 */
export interface UnclaimedTasksResult {
    /** Wake actions to execute */
    wakeActions: WakeAction[];
    /** WorkItem IDs that triggered wake actions */
    unclaimedWorkItemIds: string[];
}
/**
 * Detects WorkItems in 'queued' status that have gone unclaimed for longer
 * than the threshold, and selects the best dormant agent to wake.
 *
 * Selection algorithm scores agents by:
 *   skill_match (0–40) + urgency (0–30) + context_familiarity (0–20) - load_penalty (0–20)
 *
 * Wake strategy depends on agent status:
 *   - suspended → rehydrate (AgentSuspendService.rehydrateAgent())
 *   - inactive → start (start-agent API)
 *
 * @param workItems - All queued WorkItems in the pool
 * @param agentHealthMap - Map of agent session → health info
 * @param unclaimedThresholdMs - How long unclaimed before triggering wake (default: 2 min)
 * @returns Wake actions and affected WorkItem IDs
 */
export declare function detectUnclaimedTasks(workItems: WorkItem[], agentHealthMap: Map<string, AgentHealth>, unclaimedThresholdMs?: number): UnclaimedTasksResult;
/**
 * Scores and selects the best agent to handle a WorkItem.
 *
 * Score = skill_match (0–40) + urgency (0–30) + context_familiarity (0–20) - load_penalty (0–20)
 *
 * @param workItem - The WorkItem needing an agent
 * @param agents - Candidate agents (suspended or inactive)
 * @param waitTimeMs - How long the WorkItem has been waiting
 * @param excludeAgents - Agents already scheduled for wake in this pass
 * @returns Best agent with score, or null if no suitable agent
 */
export declare function selectBestAgent(workItem: WorkItem, agents: AgentHealth[], waitTimeMs: number, excludeAgents?: Set<string>): {
    agent: AgentHealth;
    score: number;
    breakdown: AgentScoreBreakdown;
} | null;
/**
 * Computes the score breakdown for a single agent–WorkItem pair.
 *
 * @param workItem - The WorkItem being matched
 * @param agent - The candidate agent
 * @param waitTimeMs - How long the WorkItem has been waiting
 * @returns Score breakdown
 */
export declare function computeAgentScore(workItem: WorkItem, agent: AgentHealth, waitTimeMs: number): AgentScoreBreakdown;
/**
 * Detects WorkItems in 'failed' status that still have remaining retries
 * and can be automatically re-queued for another attempt.
 *
 * @param workItems - Active WorkItems to inspect
 * @returns Corrections for retryable items and their IDs
 */
export declare function detectRetryableFailedWorkItems(workItems: WorkItem[]): {
    corrections: ReconcileCorrection[];
    retriedIds: string[];
};
/**
 * Detects WorkItems in 'blocked' status whose blocking dependencies
 * (tracked via dependsOn array) have all reached terminal status.
 * These items can be unblocked and re-queued.
 *
 * @param workItems - Active WorkItems to inspect
 * @param workItemMap - Map of all WorkItems by ID for dependency lookup
 * @returns Corrections for unblocked items and their IDs
 */
export declare function detectDependencyResolvedWorkItems(workItems: WorkItem[], workItemMap: Map<string, WorkItem>): {
    corrections: ReconcileCorrection[];
    unblockedIds: string[];
};
//# sourceMappingURL=reconcile-rules.d.ts.map