import type { RepoMeshNodePolicy, RepoMeshSchedulingStrategy } from '../repo-mesh-types.js';
import type { MeshWorkQueueEntry } from './mesh-work-queue.js';
/** Per-(node, provider) cap and its current consumption. */
export interface MeshNodeProviderSchedulingRuntime {
    providerType: string;
    /** Declared maxParallel cap for this (node, provider). Omitted when uncapped. */
    maxParallel?: number;
    /** Active (status='assigned') tasks on this node claimed by this provider. */
    activeAssigned: number;
    /** True when activeAssigned has reached maxParallel (a further claim is refused). */
    capReached: boolean;
}
/** Per-node scheduling-runtime projection. */
export interface MeshNodeSchedulingRuntime {
    nodeId: string;
    /** Active (status='assigned') task count on this node — the least-loaded rank key. */
    load: number;
    /** Soft scheduling priority (PRIORITY rank key; higher = preferred). */
    schedulingPriority: number;
    /** Per-node concurrent-session cap, when configured. */
    maxConcurrentSessions?: number;
    /**
     * Per-(node, provider) caps + consumption, when the node's slots declare a
     * maxParallel for any provider. (Field name kept for dashboard back-compat;
     * the cap source is now slots[].maxParallel, not the removed providerRoles.)
     */
    providerRoles?: MeshNodeProviderSchedulingRuntime[];
    /**
     * True when the node currently cannot claim a NEW write (non-readonly) task —
     * either the global write cap is exhausted or a node-local gate (active write
     * assignment / a fully-consumed provider cap / session cap) blocks it.
     */
    capReached: boolean;
    /** Structured reasons backing capReached (empty when the node can take work). */
    capReasons: string[];
}
/** Mesh-level scheduling-runtime projection. */
export interface MeshSchedulingRuntime {
    /** Resolved tie-break strategy (defaults to 'first_eligible'). */
    strategy: RepoMeshSchedulingStrategy;
    /** Effective global write-task parallel cap (clamped). */
    maxParallelTasks: number;
    /** Derived read-only diagnosis cap (max(2, 2× write cap)) — mirrors the claim path. */
    maxReadonlyParallelTasks: number;
    /** Current global write (non-readonly) assigned-task load. */
    activeWriteAssigned: number;
    /** Current global read-only assigned-task load. */
    activeReadonlyAssigned: number;
    /** True when the global write cap is exhausted (no new write task can launch). */
    globalWriteCapReached: boolean;
    /** True when the read-only diagnosis cap is exhausted. */
    globalReadonlyCapReached: boolean;
    /** Per-node projections, in mesh config order. */
    nodes: MeshNodeSchedulingRuntime[];
}
interface MeshLike {
    policy?: {
        schedulingStrategy?: unknown;
        maxParallelTasks?: unknown;
    } | null;
    nodes?: Array<{
        id?: string;
        nodeId?: string;
        node_id?: string;
        policy?: RepoMeshNodePolicy | null;
        isLocalWorktree?: boolean;
    }> | null;
}
/**
 * Build the scheduling-runtime projection for a mesh from its config + a snapshot of
 * its work queue. Pure and side-effect free — safe to call on any read path.
 *
 * The derivation deliberately mirrors maybeAutoLaunchOneQueueSession's gates so the
 * exposed "capReached/capReasons" match why a real claim would be refused:
 *   • global write cap   → activeWriteAssigned >= maxParallelTasks
 *   • node write conflict→ a node already holding an assigned write task (worktree isolation)
 *   • provider cap       → a (node, provider) at its declared maxParallel
 * It does NOT re-evaluate eligibility/required-tags (those are task-specific); it only
 * reports the capacity picture, which is what an operator needs to read load balance.
 */
export declare function buildMeshSchedulingRuntime(mesh: MeshLike | null | undefined, queue: MeshWorkQueueEntry[]): MeshSchedulingRuntime;
export {};
