import type { DepType, NodeStatus, RudderGraph, RunRecord, TaskNode } from "./types.js";
export declare function graphPath(repoRoot: string): string;
/**
 * Read the graph for a repo. Returns an empty graph (not null) when graph.json
 * is absent or malformed, so callers never special-case the first-write path.
 */
export declare function readGraph(repoRoot: string): Promise<RudderGraph>;
/**
 * Content-hashed node id: stable mergeable key from the title, the current
 * time, and a nonce so two nodes with the same title never collide.
 */
export declare function newNodeId(title: string): string;
export declare function newEdgeId(from: string, to: string): string;
/**
 * Atomically read-modify-write the graph under the per-path lock (daemon-only
 * writer). The transform mutates/returns the graph; keys are sorted and the
 * updatedAt stamp refreshed before write so diffs stay minimal.
 */
export declare function updateGraph(repoRoot: string, fn: (graph: RudderGraph) => RudderGraph | void): Promise<RudderGraph>;
/** A dependency edge in the mirror payload: this node depends `on` another. */
export type MirrorDep = {
    on: string;
    type: DepType;
};
/** One node in the mirror payload. Only `id`, `title`, `status`, and `deps` are
 * load-bearing; the rest enrich the board projection when the TUI knows them. */
export type MirrorNode = {
    id: string;
    title: string;
    prompt?: string;
    status: string;
    runId?: string;
    jjChangeId?: string;
    backend?: string;
    model?: string;
    effort?: string;
    worktreePath?: string;
    deps?: MirrorDep[];
};
export type MirrorPayload = {
    nodes?: MirrorNode[];
};
/** Map a TUI status string onto the DAG-level NodeStatus vocabulary. Unknown
 * values fall back to "planned" so a malformed payload never crashes the mirror. */
export declare function mirrorNodeStatus(status: string | undefined): NodeStatus;
/**
 * Project a TUI plan payload into the graph, IN PLACE. Upserts every node in the
 * payload (mapping TUI status -> NodeStatus), rebuilds each node's incoming edges
 * from its deps, and PRUNES any graph node/edge no longer present in the payload
 * so the mirror exactly reflects the current TUI plan (not an append-only log).
 * Pure (no IO): callers wrap it in updateGraph. The prompt is taken from the
 * payload when present (so a re-plan that reuses a node id cannot leave a stale
 * prompt from the previous plan); source/createdAt are preserved on upsert;
 * everything else is overwritten from the payload.
 */
export declare function mirrorPlanIntoGraph(graph: RudderGraph, payload: MirrorPayload): RudderGraph;
export declare function hardParents(graph: RudderGraph, id: string): string[];
export declare function softParents(graph: RudderGraph, id: string): string[];
/**
 * Judge parents of a node: the variants a judge node compares. A judge edge
 * gates readiness on the variant REACHING review (finishing its work), not on
 * merge. Fan-out-and-judge uses this so the judge launches once every variant
 * has produced a diff to compare.
 */
export declare function judgeParents(graph: RudderGraph, id: string): string[];
/**
 * Direct dependents (children) of a node: nodes with an edge `from` this id.
 */
export declare function dependents(graph: RudderGraph, id: string): string[];
/**
 * A node is ready when it is still `planned` AND every hard-dep parent has
 * merged AND every judge parent has reached review or merged. Soft parents
 * never block readiness. A judge node (which has judge parents) launches only
 * once all of its variants have finished their work (review or merged), so it
 * has every variant's diff to compare.
 */
export declare function isReady(graph: RudderGraph, node: TaskNode): boolean;
/**
 * Every node eligible to launch: planned with all hard parents merged.
 */
export declare function readyNodes(graph: RudderGraph): TaskNode[];
/**
 * The frontier: non-merged leaves. A node is on the frontier when it is not
 * merged AND has no non-merged child (every dependent is already merged, or it
 * has no dependents). Injection reconciliation attaches new nodes here.
 */
export declare function frontier(graph: RudderGraph): TaskNode[];
/**
 * Project a worker-owned RunStatus into the DAG-level NodeStatus. With no run
 * yet, the node keeps its deps-derived status (planned/ready/blocked); the
 * scheduler decides those. Mirrors plan section 2's projection table.
 */
export declare function projectNodeStatus(node: TaskNode, run?: RunRecord): NodeStatus;
