import type { TaskEvidence } from './completion-gate.js'; import type { TaskReferences } from './packet.js'; import type { OrgTask, OrgTaskStatus, SplitChild } from './task-dag-types.js'; export type { OrgTask, OrgTaskStatus, SplitChild } from './task-dag-types.js'; export { MAX_TASK_BRIEF } from './task-dag-types.js'; /** #319: the same "this task is finished" predicate the DAG's own guards use, * for callers outside this module (decisions.ts refuses a second close). */ export declare function isTerminalStatus(status: OrgTaskStatus): boolean; export declare class TaskDag { private tasks; private counter; add(title: string, assignee: string, deps?: string[], loadout?: string, brief?: string, references?: TaskReferences): OrgTask; complete(id: string, result?: string): OrgTask[]; /** ADR-O001 D4: record one failed completion-evidence check against a task * and return its running total. Per task on purpose — a cap counted * org-wide would escalate a healthy task because unrelated ones failed. */ recordEvidence(id: string, ev: TaskEvidence): void; recordEvidenceFailure(id: string): number; fail(id: string, reason?: string): void; markRunning(id: string): void; /** Put a 'running' task back to 'ready' so dispatchReadyTasks re-sends it. * Used on checkpoint resume for tasks whose assignee's session was not * resumed: the "[task:…]" message was consumed by a session that no longer * exists, so nothing would ever pick the task up again otherwise. */ requeue(id: string): void; /** Mark a task as waiting on a real-world time, not on other tasks. Only * valid from 'running' (a role already working it discovers it can't * proceed further right now) — a 'ready'/'pending' task should just stay * that way until its deps clear — or from 'blocked', which re-blocks it * with a new time (the answer to a #329 re-check that is still waiting). */ block(id: string, untilMs: number, reason?: string, recheckEveryMs?: number): OrgTask; /** #343: hold a 'ready' or 'running' task whose assignee cannot take it * (its session was closed for budget) as 'blocked' with the reason, so * org_tasks shows why instead of an ordinary unstarted task. Not a time * block: no blockedUntil, so it is never auto-resumed and never counts as * a legitimate wait. Re-holding a held task only refreshes the reason. */ holdForAssignee(id: string, reason: string): OrgTask; /** Put a task held by `holdForAssignee` back to 'ready' so dispatchReadyTasks * sends it again. False for anything else, including a time block. */ releaseAssigneeHold(id: string): boolean; /** Transition every task whose block has expired back to 'running', so its * assignee gets nudged that it's time to resume. Called by the idle * watchdog on every tick — cheap no-op when nothing has expired. */ unblockExpired(now: number): OrgTask[]; /** True if there is genuinely nothing else dispatchable right now — every * non-terminal task is blocked on a real-world time still in the future. * This is the watchdog's signal to skip nudging (same treatment as a * pending gate). Scoped to the whole DAG rather than a single task on * purpose: if ANY task is pending/ready/running, or blocked with a time * that's already passed (should have auto-resumed), there is real * outstanding work and nudging is still meaningful — a single long-lived * block on one task (e.g. a feature deferred for weeks) must not silence * the watchdog for the entire org while other roles sit genuinely idle. */ hasActiveBlock(now: number): boolean; /** When `hasActiveBlock` next goes false: the EARLIEST active block expiry * (at that moment `unblockExpired` resumes that task, so there is * dispatchable work again), or null if nothing is blocking. * * ADR-O001 D4 — the idle watchdog records a deadline on every hold, and a * time-blocked task already has a real one: the time the asker named. It * gets that, not an arbitrary timeout, so a block legitimately set hours * out is not nudged about in the meantime. */ activeBlockUntil(now: number): number | null; split(parentId: string, children: SplitChild[]): OrgTask[]; merge(sourceId: string, targetId: string): OrgTask; cancel(id: string, reason?: string): OrgTask[]; ready(): OrgTask[]; /** #302: true if any task has not reached a terminal status. Paired with * `hasActiveBlock` at call sites, never used alone to mean "nothing left * to do" — an empty DAG also returns false here, same as * `hasActiveBlock`'s own empty-DAG edge (see its doc comment), so the * caller must not read a `false` in isolation as "the goal is achieved". */ hasPendingWork(): boolean; /** #302: how many tasks have not reached a terminal status — the count a * truth-gated stop record (`daemon.ts`'s `finishStop`) attaches to an * automated stop, so "the run ended with N tasks still outstanding" is a * fact in the record, not just a boolean. */ pendingTaskCount(): number; get(id: string): OrgTask | undefined; all(): OrgTask[]; toJSON(): OrgTask[]; static fromJSON(data: OrgTask[]): TaskDag; private promoteReady; private hasCycle; } //# sourceMappingURL=task-dag.d.ts.map