/** * Generic task-graph walker — produces a typed {@link TreeResponse} that * covers BOTH `parent_id` edges AND `task_relations.relation_type='groups'` * edges from any root, recursively to full depth. * * Used by `cleo tree ` to surface the complete task graph rooted at a * given task. Walks downward via parent edges + groups edges, and emits a * separate ancestor chain (parent edges only) so the caller sees where the * root sits in the broader hierarchy. * * @epic T10114 * @task T10134 * @see ADR-077-human-render-contract.md * @see ADR-073-above-epic-naming.md §1 — Saga ↔ Epic linkage */ import type { FlatTreeNode, TaskPriority, TreeResponse } from '@cleocode/contracts'; /** * Edge type that placed a node under its parent in the rendered tree. * * - `'parent'` — node was reached via the `parent_id` column (default hierarchy). * - `'groups'` — node was reached via `task_relations.relation_type='groups'` * (saga membership edge). * - `'root'` — the node is the rendered root; it has no incoming edge. * * Used by the renderer to prefix saga-membership rows with * {@link RelationIcon.GROUPS} (`⊂`) so the user can distinguish a parent edge * from a groups edge at a glance. */ export type GenericTreeEdgeType = 'parent' | 'groups' | 'root'; /** * Per-node metadata carried in {@link FlatTreeNode.metadata} for every row * produced by {@link buildGenericTaskTree}. * * Mirrors the dependency-annotation fields exposed by the legacy * `FlatTreeNode` in `task-tree.ts` so the CLI's existing `--withDeps` and * `--blockers` annotations continue to work without a second DB round-trip. */ export interface GenericTreeMetadata { /** * Edge that placed this node under its parent. `'root'` for the rendered * root itself. */ readonly edgeType: GenericTreeEdgeType; /** Task priority. Defaulted to `'medium'` when the source row omits it. */ readonly priority: TaskPriority; /** Raw `depends` IDs from the task record. Empty when the task has no deps. */ readonly depends: ReadonlyArray; /** * Open (unresolved) dependency IDs that are currently blocking this task. * * A dependency is considered open when its status is not `'done'` or * `'cancelled'`. */ readonly blockedBy: ReadonlyArray; /** * Whether this task is immediately actionable — `true` when `blockedBy` * is empty AND the task's status is `'pending'` or `'active'`. */ readonly ready: boolean; /** * Full transitive blocker chain. Populated only when the caller requests * blocker enrichment (`withBlockers: true`). */ readonly blockerChain?: ReadonlyArray; /** * Leaf-level blockers — root-cause tasks that must be resolved first. * Populated only when the caller requests blocker enrichment. */ readonly leafBlockers?: ReadonlyArray; } /** * Result of {@link buildGenericTaskTree}. * * The returned `tree` envelope walks downward from `rootId` (covering both * parent and groups edges). The optional `ancestors` array carries the upward * chain from `rootId` to the eventual root of the broader hierarchy — strict * parent-only walk, useful when `rootId` is a leaf task and the caller wants * to show context above it. */ export interface GenericTreeResult { /** Flat, pre-order tree envelope rooted at the requested task. */ readonly tree: TreeResponse; /** * Ancestor chain from the rendered root upward through parent_id edges. * Ordered nearest-first (immediate parent at index 0). Empty when the * rendered root has no parent. */ readonly ancestors: ReadonlyArray>; } /** * Options for {@link buildGenericTaskTree}. */ export interface BuildGenericTreeOptions { /** * Enrich each node with `blockerChain` and `leafBlockers` metadata. * Equivalent to the `withBlockers` flag on the legacy `coreTaskTree`. * * @defaultValue false */ readonly withBlockers?: boolean; } /** * Build the typed tree envelope rooted at `rootId`, walking BOTH parent edges * AND groups edges to full depth. Cycle-safe via a single visited set spanning * both edge types. * * Children of a node are emitted in this stable order: * 1. `parent_id` children (sorted by `position ASC` like the legacy walker) * 2. `groups` members (in edge-insertion order) * * The two sets do not overlap in well-formed data because sagas hold members * via `groups` edges instead of `parent_id`. If a future schema change allows * overlap, the visited set guarantees each node is emitted exactly once and * the first edge wins. * * Each emitted node carries {@link GenericTreeMetadata} so the renderer (and * downstream `--withDeps`/`--blockers` annotations) has everything it needs * without a second DB round-trip. * * @param projectRoot - Absolute project root used to resolve the data accessor. * @param rootId - Task ID to root the tree at. Required — the legacy * "all roots" walk is intentionally not supported here * because `cleo tree ` always takes a positional ID. * @param opts - Optional enrichment flags. * @throws `Error` with `E_NOT_FOUND` semantics when `rootId` is unknown. */ export declare function buildGenericTaskTree(projectRoot: string, rootId: string, opts?: BuildGenericTreeOptions): Promise; //# sourceMappingURL=generic-tree.d.ts.map