/** * lock — per-change exclusive lock for task runtime (G-026 M1, ADR-0004 #10). * * Semantics: * - Only one orchestrator may hold a change's lock at a time. * - Owner proof: pid + nonce + touch heartbeat. A stale lock (last touch older * than maxHoldMs) may be broken only by an explicit breakStale() call. * - acquire() never silently steals a live lock; on contention it throws * LockHeldError with the owner information. * - Lock directory is a directory (task-runtime.lock/) so we can hold owner * metadata inside it atomically (owner.json) and touch a heartbeat file. * - A corrupted lock (unreadable owner.json) → fail closed, never assumed free. */ import { type TaskRuntimeLayout } from "./file-layout.js"; export interface LockOwner { pid: number; nonce: string; acquiredAt: string; /** Heartbeat timestamp refreshed while holding (ISO). */ lastTouchAt: string; /** Command line / operation id for audit. */ operationId?: string; } export declare class LockHeldError extends Error { readonly owner: LockOwner | null; constructor(owner: LockOwner | null); } export interface AcquireOptions { /** Max ms the lock may be held before it is considered stale (default 24h). */ maxHoldMs?: number; /** Operation id for audit. */ operationId?: string; /** When true, break a stale lock instead of throwing (default false). */ breakStaleOnAcquire?: boolean; } export declare class TaskRuntimeLock { readonly layout: TaskRuntimeLayout; readonly maxHoldMs: number; constructor(layout: TaskRuntimeLayout, maxHoldMs?: number); private ownerFile; private heartbeatFile; /** True when the lock directory exists and owner.json parses. */ isHeld(): Promise; private isFresh; private readOwnerUnsafe; /** * Acquire the exclusive lock. * - free/stale → create dir + owner.json + heartbeat (atomic mkdir) * - live → throw LockHeldError * - stale + breakStaleOnAcquire → break and acquire */ acquire(opts?: AcquireOptions): Promise; /** Refresh heartbeat (bump lastTouchAt). */ touch(): Promise; /** * Release the lock. Only the holder identified by pid+nonce may release; * any other owner → LockHeldError (prevents accidental clobber). */ release(owner: LockOwner): Promise; /** Break a stale lock (explicit recovery path; no ownership proof beyond staleness). */ breakStale(): Promise; }