import { withDataBranch } from './data-branch.js';
import type { PlanAssignment } from './auto-pm.js';
/** A ticket filename's lock sibling, e.g. `a.md` → `a.lock.md`, without the directory. */
export declare function ticketLockName(ticket: string): string;
/** What a lock file holds: the claim line and nothing else, matching the ticketing format. */
export declare function ticketLockContent(agentId: string): string;
/** The agent a lock file names, or undefined for content that is not a claim line. */
export declare function ticketLockHolder(md: string): string | undefined;
/** The commit a batch of locks lands as. Names the count so the history reads as what happened. */
export declare function lockMessage(count: number): string;
/** The commit a manual release lands as, naming the ticket freed. */
export declare function releaseMessage(ticket: string): string;
/** The commit an abandoned claim's release lands as (#1583), naming why the daemon freed it. */
export declare function abandonedReleaseMessage(ticket: string): string;
/** Injectable seams so every operation is unit-testable off disk and git. */
export interface TicketLockDeps {
    /** Write one lock file (default `fs.writeFile`). */
    write?: (path: string, content: string) => Promise<void>;
    /** Read one sibling (default `fs.readFile`); a rejection reads as "absent". */
    read?: (path: string) => Promise<string>;
    /** Delete one lock file (default `fs.rm`). */
    remove?: (path: string) => Promise<void>;
    /** The data-branch write funnel (default {@link withDataBranch}); a test's fake stands in. */
    funnel?: typeof withDataBranch;
    /** Progress line. */
    log?: (message: string) => void;
}
/**
 * Which side of a ticket's life a batch claims for (#1420). A `plan` batch is about to *write*
 * plans, so an existing `.plan.md` means the work it came for is already done and the ticket is
 * skipped. A `drain` batch is about to *implement* a plan — the `.plan.md` is its input, not a
 * competing claim — so only an existing `.lock.md` stands in its way.
 */
export type TicketLockPhase = 'plan' | 'drain';
/**
 * Claim `assignments`' tickets for their agents: one `.lock.md` per ticket, written on the data
 * branch in one funneled cycle (#1582) — the funnel commits the batch and pushes the branch. The
 * cycle re-runs the checks against origin's state when a push loses a race, so a ticket whose
 * lock (or, for a `plan` batch, whose plan — see {@link TicketLockPhase}) appeared meanwhile is
 * skipped, not overwritten: an existing file is someone's claim or someone's work, and either
 * outranks this batch.
 *
 * Resolves the subset actually locked. A batch that could not land at all resolves `[]`; a batch
 * that committed but could not *push* is kept and resolved as locked — the commit still guards
 * every agent forked from this machine, which is the common case, and the sweep should not stand
 * a healthy local fan-out down over a network blip. The push is what closes the cross-machine
 * window (#1320), so its failure is logged rather than swallowed.
 *
 * Never throws: this runs on a background tick with nothing to catch it.
 */
export declare function acquireTicketLocks(cwd: string, assignments: readonly PlanAssignment[], deps?: TicketLockDeps, phase?: TicketLockPhase): Promise<PlanAssignment[]>;
/**
 * What a release did: freed the ticket, found nothing to free, found someone else's claim
 * (`heldBy` releases only, #1583), or could not commit.
 */
export type ReleaseTicketLockResult = 'released' | 'no-lock' | 'not-holder' | 'error';
/**
 * Free one ticket's `.lock.md` by hand (#1420): the dashboard's answer to a dead agent, now that
 * no timer releases locks. One funneled data-branch cycle (#1582); a release that cannot land
 * reports `error` and changes nothing — the funnel restores the checkout, so the committed state
 * keeps telling the truth about the claim.
 */
export declare function releaseTicketLock(cwd: string, ticket: string, deps?: TicketLockDeps, opts?: {
    /**
     * Free the lock only while it still names this exact agent (#1583): the daemon releasing a
     * claim it minted for an agent that ended with nothing to hand off. A lock naming anyone
     * else is someone's live claim — re-locked after a manual release, say — and outranks the
     * cleanup. Absent (the dashboard button), whoever holds the lock is released.
     */
    heldBy?: string;
}): Promise<ReleaseTicketLockResult>;
//# sourceMappingURL=ticket-locks.d.ts.map