import type { BlockRef, CoreAC, CoreIssue, CoreRoot } from './engine.js'; export declare const isPassed: (ac: CoreAC) => boolean; export interface BlockingOpts { isIssueDone?: (issue: CoreIssue) => boolean; } export interface BlockNode { kind: 'issue' | 'ac'; key: string; issue: CoreIssue; ac?: CoreAC; } /** The key for a block reference's target node (an issue id, or `issue:ac`). */ export declare function refKey(ref: BlockRef): string; /** Every node in the tracker (issues and ACs), keyed by universal id. Issue ids never * contain ':' and AC keys always do, so the two key spaces never collide. */ export declare function nodeIndex(root: CoreRoot): Map; /** A node is satisfied (a met blocker) when its work is complete: an AC when passed; an * issue when all its ACs are passed — or, for an AC-less issue, when its terminal * status says so (see BlockingOpts.isIssueDone). */ export declare function nodeSatisfied(node: BlockNode, opts?: BlockingOpts): boolean; export interface GraphOpts { containment?: boolean; } /** The unified dependency graph: for each node, the set of nodes that must land before * it (its direct dependencies). Edges come from every authored direction — * AC `blocked-by` Y → AC depends on Y; AC `blocks` Y → Y depends on AC; * issue `blocked-by` J → issue depends on J; issue `blocks` J → J depends on issue. * With `containment`, an issue also depends on each of its own ACs ("an issue is done * only when its ACs are"): this is used ONLY for cycle detection, where it surfaces * cross-level deadlocks; readiness/gate omit it so an in-progress issue doesn't read as * "blocked" by its own open work. Edges to non-existent nodes and self-edges are * dropped (the referent/self rules report those separately). */ export declare function dependencyGraph(root: CoreRoot, { containment }?: GraphOpts): Map>; /** Every dependency cycle (each a list of node keys forming a loop). A cycle is an * impossible-to-satisfy constraint, so a non-empty result is a hard error. Computed * with containment, so a cross-level deadlock (A's AC waits on all of B, B's AC waits * on all of A) is caught. */ export declare function blockCycles(root: CoreRoot): string[][]; export interface NodeBlockStatus { /** true when any (transitive) dependency is not yet satisfied. */ blocked: boolean; /** the unsatisfied nodes upstream in the dependency closure — what's holding it up. */ blockers: BlockRef[]; } /** Per node, its transitive blocked state: the closure of upstream dependencies that * are not yet satisfied. `blocked` is true when that set is non-empty; a node with no * unmet upstream work is actionable now. Cycle-safe (each upstream node visited once) * and containment-free (an issue is "blocked" only by EXTERNAL unmet work, not its own * open ACs). */ export declare function blockStatuses(root: CoreRoot, opts?: BlockingOpts): Map; /** Per node, the NEAREST unmet upstream node(s) — the first unmet hop along each edge out of * the node, as opposed to `blockStatuses`' full transitive closure. Walks the same graph: from * each direct dependency, an UNSATISFIED one is reported and the walk stops there (no point * naming what's behind an already-named wall); a SATISFIED one is transparent — the walk * continues through it, since a satisfied node can still sit in front of unmet work further * upstream (e.g. it was completed out of order — a `completionViolations` case — or it simply * has no bearing on its own upstream once done). Cycle-safe (each node visited once per walk, * same as blockStatuses). This is the "which blocker, and its status" view a stalled dispatch * wave is diagnosed from — `blockStatuses` alone only says THAT something is blocked. */ export declare function nearestBlockers(root: CoreRoot, opts?: BlockingOpts): Map; /** The issue-level "dispatch frontier" view (ZTB-30): per issue, whether it can be worked on * RIGHT NOW, and if not, the nearest external blocker(s) holding it up. Two things feed * "blocked" here, deliberately more than `blockStatuses(root).get(issue.id)` alone: * 1. the issue's OWN issue-level relations (`blocked-by`/`blocks` lines) — exactly what * `blockStatuses`/`nearestBlockers` already compute for the issue's node key. * 2. any of the issue's OWN acceptance criteria blocked on work OUTSIDE the issue (an AC's * `blocked-by` naming another issue, or a specific AC of another issue). From a dispatch * standpoint a subagent assigned to this issue hits that wall the moment it reaches that * AC, so the issue isn't really actionable yet either — even though issue-level * `blockStatuses` (containment-free by design) doesn't see it, since that's an edge OFF an * AC node, not off the issue node. * An AC blocked on ANOTHER AC of the SAME issue is explicitly NOT external — that's ordinary * in-issue sequencing (do the other AC first, in the same session), not something outside the * issue holding it up, so it's excluded — the same "not blocked by its own open ACs" spirit * `blockStatuses` already applies at the issue level. */ export declare function issueFrontier(root: CoreRoot, opts?: BlockingOpts): Map; export interface GateViolation { node: BlockNode; dep: BlockNode; } /** Out-of-order completions: a satisfied node that directly depends on an unsatisfied * one. Over the unified graph, so it fires across levels and both edge directions. */ export declare function completionViolations(root: CoreRoot, opts?: BlockingOpts): GateViolation[]; export interface RefProblem { issueId: string; acId: string; ref: BlockRef; kind: 'missing' | 'self'; } /** AC blocker references that don't name a real node, or that name the AC itself. */ export declare function blockerRefProblems(root: CoreRoot): RefProblem[]; export interface RawBlockRef { issue: string; ac: string; bare: boolean; } /** Parse one authored blocker token against the issue it was written in. */ export declare function parseBlockToken(token: string, scopeIssue: string): RawBlockRef | null; type ParsedIssue = { id: string; acceptanceCriteria: Array<{ id: string; blockedBy?: RawBlockRef[]; blocks?: RawBlockRef[]; }>; }; /** Resolve every parsed blocker to its final form, now that all issues/ACs are known. * A bare token is a local AC if one exists, otherwise an issue if one exists, otherwise * a (dangling) local AC the referent rule will flag. Mutates the parsed issues in place, * replacing RawBlockRef with the stored BlockRef shape. */ export declare function normalizeBlockRefs(issues: ParsedIssue[]): void; export {};