/**
* Workspace transition lock.
*
* One cross-process lock serializes mutation-class operations on a workspace.
* A plan-family apply acquires it after confirmation — planning, network
* acquisition, preview, and the confirmation decision stay lock-free — then
* revalidates its candidate and applies while holding it; the workspace
* transaction reuses the invocation's hold, or acquires its own when no
* plan-family hold exists. A contending invocation waits with a visible
* reason up to a bound, then terminates blocked with a machine-readable
* reference to the holder.
*
* Each acquisition records a distinct owner token in the holder metadata.
* Ownership is proven only by an exact token match — never inferred from a
* pid or from missing or unreadable metadata — and a holder write that fails
* releases the acquisition instead of holding anonymously.
*
* The lock file lives at `.axm/tmp/workspace-transition.lock`; its path is
* load-bearing for operational tooling and must not move casually.
*
* Acquisition is atomic with respect to interruption: the narrow region from
* the library granting the hold through finalizer registration is masked, so
* an interrupt can never strand a granted lock, while contention waits stay
* interruptible. Only the lock-is-held error class is absorbed by the bounded
* wait; any other acquisition error surfaces immediately as a typed failure.
*
* @experimental This API is unstable and may change without notice.
*/
import * as Effect from "effect/Effect";
import * as FileSystem from "effect/FileSystem";
import * as Option from "effect/Option";
import * as Path from "effect/Path";
import type * as Scope from "effect/Scope";
import { type AppError } from "../app-error/index.js";
export declare const TRANSITION_LOCK_FILENAME = "workspace-transition.lock";
/** How long a contending invocation serializes behind the holder. */
export declare const TRANSITION_WAIT_BOUND_MILLIS = 60000;
export interface TransitionLockHolder {
readonly command: string;
readonly pid: number;
readonly candidateId?: string;
}
declare const WorkspaceTransitionCompromised_base: new = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
readonly _tag: "WorkspaceTransitionCompromised";
} & Readonly;
/**
* The hold is no longer provably owned: `proper-lockfile` failed to confirm
* ownership within the staleness window, so a contender may already have
* reclaimed the lock. Any further durable write by the original owner —
* mutation and restoration alike — could overwrite a successor's work.
*/
export declare class WorkspaceTransitionCompromised extends WorkspaceTransitionCompromised_base<{
readonly workspaceDir: string;
readonly lockPath: string;
readonly cause: unknown;
}> {
}
/** The live view of one acquisition this process currently holds. */
export interface HeldWorkspaceTransition {
/** Fails when the hold is compromised; never succeeds and never ends otherwise. */
readonly compromised: Effect.Effect;
/** Synchronous probe for boundaries that cannot race, such as restoration. */
readonly isCompromised: () => boolean;
}
export declare const isWorkspaceTransitionHeldByThisInvocation: (workspaceDir: string) => boolean;
/** The live hold for a workspace this process acquired, when one exists. */
export declare const heldWorkspaceTransition: (workspaceDir: string) => HeldWorkspaceTransition | undefined;
export declare const transitionLockPath: (path: Path.Path, workspaceDir: string) => string;
export interface TransitionContention {
/** The holder recorded by the invocation that owns the lock, when readable. */
readonly holder: Option.Option;
readonly waitedMillis: number;
}
/**
* Acquire the workspace transition lock, waiting up to the bound while
* another invocation holds it. Resolves `None` when acquired (the release is
* a scope finalizer) and `Some(contention)` when the bound elapsed.
*/
export declare const acquireWorkspaceTransitionLock: (args: {
readonly workspaceDir: string;
readonly holder: TransitionLockHolder;
readonly waitBoundMillis?: number;
/** Called once when the invocation starts waiting on another holder. */
readonly onWaiting?: (holder: Option.Option) => Effect.Effect;
/** Staleness/refresh override for deterministic compromise tests only. */
readonly timingMillis?: {
readonly stale: number;
readonly update: number;
};
}) => Effect.Effect, AppError, FileSystem.FileSystem | Path.Path | Scope.Scope>;
export {};
//# sourceMappingURL=transition-lock.d.ts.map