framework
Version:
The (AI) Framework: turnkey, zero-config AI orchestration that wraps a coding-agent CLI (Claude Code) as a black box and takes you from an idea to a running app. Vite for AI.
72 lines • 4.53 kB
TypeScript
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