/**
 * `TicketBoard` — the swarm's unit-of-work store.
 *
 * A board is the shared to-do list a crew of agent teammates pulls from. Each
 * piece of work is a {@link Ticket}: a small, schema-validated record with a
 * lifecycle (`open → claimed → done | failed`, plus a `blocked` side-state) and
 * an optional set of `deps` on other tickets. The whole board lives in one JSON
 * file, mediated by the kernel's {@link JsonCell}, so every mutation is a single
 * guarded read-modify-write — the board never touches `node:fs` locking, temp
 * renames, or coercion itself. It is, as intended, a thin domain wrapper over
 * `JsonCell.mutate`.
 *
 * The dependency relation is a first-class concern. `deps` form a directed graph
 * ("A must be `done` before B is workable"); {@link TicketBoard} keeps that graph
 * honest in two ways, both delegated to {@link DepGraph}:
 *
 *   - **Edges once.** A dependency declared twice contributes a single edge; the
 *     graph carries no parallel edges.
 *   - **No cycles.** {@link TicketBoard.add} rebuilds the graph including the
 *     incoming ticket and rejects the insert with a `conflict` fault if it would
 *     introduce a cycle — a dependency loop can never reach disk.
 *
 * Readiness falls out of that graph: {@link TicketBoard.ready} returns the `open`
 * tickets whose every dependency has reached `done`. Those are the tickets a
 * member may safely {@link TicketBoard.claim}; claiming is atomic (it runs inside
 * the cell's guarded transform), so two members racing for the same ticket cannot
 * both win.
 */
import { z } from "zod";
/** The lifecycle states a ticket moves through. */
export declare const ticketStatuses: readonly ["open", "claimed", "blocked", "done", "failed"];
/** A ticket's lifecycle state. */
export type TicketStatus = (typeof ticketStatuses)[number];
/**
 * One unit of work on the board.
 *
 * `status` is the lifecycle discriminant; `assignee` names the member that holds
 * a `claimed`/`blocked` ticket (and survives onto `done`/`failed` as the record
 * of who did it). `deps` are the ids of tickets that must reach `done` before
 * this one is workable. `result`/`error` capture the terminal payload of a
 * completed/failed ticket. Timestamps are epoch milliseconds.
 */
export declare const ticketSchema: z.ZodObject<{
    id: z.ZodString;
    title: z.ZodString;
    body: z.ZodString;
    status: z.ZodEnum<{
        done: "done";
        failed: "failed";
        open: "open";
        claimed: "claimed";
        blocked: "blocked";
    }>;
    assignee: z.ZodOptional<z.ZodString>;
    deps: z.ZodArray<z.ZodString>;
    result: z.ZodOptional<z.ZodString>;
    error: z.ZodOptional<z.ZodString>;
    blockReason: z.ZodOptional<z.ZodString>;
    createdAt: z.ZodNumber;
    updatedAt: z.ZodNumber;
}, z.core.$strip>;
/** A validated ticket. */
export type Ticket = z.infer<typeof ticketSchema>;
/** Fields a caller may set when adding a ticket. */
export interface TicketDraft {
    /** Short human-facing summary (required). */
    readonly title: string;
    /** Full task description; defaults to empty. */
    readonly body?: string;
    /** Ids of tickets that must complete first; defaults to none. */
    readonly deps?: readonly string[];
}
/**
 * The shared, durable ticket store a swarm coordinates through.
 *
 * Open one with {@link TicketBoard.open}; every method is a thin pass over the
 * underlying {@link JsonCell}. Mutators (`add`, `claim`, `complete`, `fail`,
 * `block`, `unblock`) run inside a single guarded transform so concurrent crews
 * sharing the file serialize correctly; readers (`get`, `list`, `ready`) are
 * lock-free.
 */
export declare class TicketBoard {
    private readonly cell;
    /** Injected clock, epoch ms — overridable for deterministic tests. */
    private readonly now;
    private constructor();
    /**
     * Open (or adopt) the board backed by the JSON file at `path`.
     *
     * `now` defaults to `Date.now`; tests may inject a deterministic clock. The
     * file is not created until the first mutation.
     */
    static open(path: string, now?: () => number): Promise<TicketBoard>;
    /**
     * Admit a new ticket, returning the persisted record.
     *
     * The ticket is born `open` with a freshly minted id and the current
     * timestamp. Its `deps` are de-duplicated; every dep must already name a
     * ticket on the board (a dangling dependency raises `not_found`). The
     * dependency graph is rebuilt including the newcomer, and the insert is
     * rejected with a `conflict` fault if it would introduce a cycle — the board
     * never persists a cyclic dependency relation.
     */
    add(draft: TicketDraft): Promise<Ticket>;
    /**
     * Atomically claim an `open` ticket for `member`.
     *
     * Moves the ticket `open → claimed` and stamps `assignee = member`. The check
     * and the write happen inside one guarded transform, so two members racing for
     * the same ticket cannot both succeed — the loser sees a `conflict` fault. A
     * ticket that is missing (`not_found`) or not currently `open` (`conflict`)
     * cannot be claimed.
     */
    claim(id: string, member: string): Promise<Ticket>;
    /**
     * Mark a `claimed` ticket `done`, recording its `result`.
     *
     * Only a `claimed` ticket may complete; completing anything else raises
     * `conflict`. Completion can unblock other tickets — see {@link ready}.
     */
    complete(id: string, result: string): Promise<Ticket>;
    /**
     * Mark a `claimed` ticket `failed`, recording the `error`.
     *
     * Only a `claimed` ticket may fail; failing anything else raises `conflict`. A
     * failed dependency never satisfies a dependent (only `done` does), so its
     * dependents stay out of {@link ready}.
     */
    fail(id: string, err: string): Promise<Ticket>;
    /**
     * Move an `open` or `claimed` ticket into the `blocked` side-state.
     *
     * Blocking records an optional human-readable `reason` and preserves the
     * `assignee`. A terminal ticket (`done`/`failed`) cannot be blocked
     * (`conflict`). Use {@link unblock} to return it to the queue.
     */
    block(id: string, reason?: string): Promise<Ticket>;
    /**
     * Return a `blocked` ticket to `open`, clearing its assignee and block reason.
     *
     * Only a `blocked` ticket may be unblocked (`conflict` otherwise). It re-enters
     * the queue unowned, so any member may {@link claim} it next.
     */
    unblock(id: string): Promise<Ticket>;
    /** Fetch one ticket by id, or `null` if the board has no such ticket. */
    get(id: string): Promise<Ticket | null>;
    /**
     * List every ticket in insertion order, optionally filtered by status.
     *
     * The order is stable across reads and process restarts (it follows the
     * persisted insertion sequence, not map iteration order).
     */
    list(status?: TicketStatus): Promise<Ticket[]>;
    /**
     * The `open` tickets that are workable right now.
     *
     * A ticket is ready when it is `open` and every id in its `deps` names a
     * ticket that has reached `done`. Tickets with unmet, failed, or still-running
     * dependencies are withheld. Results are in insertion order, so the board hands
     * work out fairly (oldest first). These are exactly the tickets a member may
     * {@link claim}.
     */
    ready(): Promise<Ticket[]>;
    /**
     * Guarded transform of a single ticket by id.
     *
     * Loads the named ticket, applies `change` (which stamps a fresh `updatedAt`),
     * and writes the whole board back — all inside one {@link JsonCell.mutate}, so
     * the read-decide-write is atomic against concurrent members. A missing id
     * raises `not_found`; `change` may throw a `conflict` to veto an illegal
     * transition.
     */
    private transition;
}
