/**
 * DB-backed needs-aware dispatch scheduler ( through).
 *
 * This module is the core of 's behavioral change: it replaces the
 * "concurrent dispatch" model with event-driven scheduling that gates ALL
 * needs edges (static-to-static, static-to-dyn-group, dyn-to-static, dyn-to-dyn).
 *
 * The scheduler is pure DB — no in-memory state. Every scheduling decision is
 * a fresh DB query against execution_jobs + execution_job_needs. This means
 * zero recovery code on orchestrator restart.
 *
 * Entry points:
 * - insertEdgesForRun: called at run start for static-to-static edges
 * - resolveGroupEdges: called on dynamic-eval completion
 * - evaluateDownstreams: called from onJobStatus(terminal)
 * - recomputeNeedsSatisfied: batch recompute after group resolution
 * - checkSchedulerInvariant: Layer 3 defensive check
 * - getFailurePropagationTargets: cascade for transitive skip
 */
import type { Kysely } from 'kysely';
import type { Database } from '../db/types.js';
import type { NeedsEntry, NeedsGroupEntry } from '@kici-dev/engine';
/** Result of evaluating downstream jobs after an upstream completes. */
export interface SchedulerResult {
    jobName: string;
    action: 'dispatch' | 'skip';
    reason?: string;
}
/** Minimal lock job shape needed by insertEdgesForRun. */
interface LockJobLike {
    readonly name: string;
    readonly needs: readonly (string | NeedsEntry | NeedsGroupEntry)[];
    readonly dependsOnGroups?: readonly string[];
}
/**
 * Insert dependency edges at run start for static-to-static needs.
 * NeedsGroupEntry items are skipped (resolved later via resolveGroupEdges).
 * Root jobs (no needs, no dependsOnGroups) are marked needs_satisfied=true.
 */
export declare function insertEdgesForRun(db: Kysely<Database>, runId: string, lockJobs: readonly LockJobLike[]): Promise<void>;
/**
 * Resolve dynamic group edges after eval completion.
 *
 * For each dependent static job and each member job name, inserts a concrete
 * edge row. Empty groups (0 members) trigger immediate needs_satisfied=true
 * for dependents.
 *
 * CRITICAL: dependentStaticJobs carries per-job ifFailed policy from the
 * NeedsGroupEntry in the lock file. Without this, all group edges would
 * silently default to 'skip'.
 */
export declare function resolveGroupEdges(db: Kysely<Database>, runId: string, groupName: string, memberJobNames: string[], dependentStaticJobs: Array<{
    jobName: string;
    ifFailed: 'skip' | 'run';
}>): Promise<void>;
/**
 * Evaluate downstream jobs after an upstream reaches terminal state.
 *
 * This is the core scheduler hook. For each downstream of the completed job:
 * 1. If upstream failed and edge has if_failed='skip', mark downstream as 'skip'
 * 2. Otherwise, check if ALL upstreams are terminal
 * 3. If all satisfied, mark needs_satisfied=true and return for dispatch
 */
export declare function evaluateDownstreams(db: Kysely<Database>, runId: string, completedJobName: string, completedStatus: string): Promise<SchedulerResult[]>;
/**
 * Batch recompute needs_satisfied for specified jobs.
 * Used after group resolution and on orchestrator restart recovery.
 * Returns scheduler results for any jobs that became ready.
 */
export declare function recomputeNeedsSatisfied(db: Kysely<Database>, runId: string, jobNames: string[]): Promise<SchedulerResult[]>;
/**
 * Layer 3: defensive scheduler invariant check.
 *
 * Finds jobs that are stuck: needs_satisfied=false, status='pending',
 * all upstreams are terminal, and no pending group resolution.
 * Returns the names of stuck jobs (empty array = no violation).
 */
export declare function checkSchedulerInvariant(db: Kysely<Database>, runId: string): Promise<string[]>;
/**
 * cascade: find all transitive downstreams that should be skipped
 * due to failure propagation. Only follows edges where if_failed='skip'.
 */
export declare function getFailurePropagationTargets(db: Kysely<Database>, runId: string, failedJobName: string): Promise<string[]>;
export {};
//# sourceMappingURL=needs-scheduler.d.ts.map