/** * Cross-process file protection: a lock, and a replace that cannot leave a * partial file behind. * * Shared state written with plain `readFileSync` → mutate → `writeFileSync` has * two failure modes that only appear under concurrency, which is exactly the * condition a fleet creates: two starts interleave and one silently discards * the other's entry, or a crash mid-write truncates the last good file. */ export interface LockDeps { now?(): number; sleep?(ms: number): Promise; /** Test hook after a complete private claim is prepared, before atomic publication. */ beforePublish?(): void | Promise; } /** The syscalls a replace depends on, injectable so faults can be forced deterministically. */ export interface WriteDeps { writeSync?(fd: number, buffer: Buffer, offset: number, length: number): number; fsyncSync?(fd: number): void; closeSync?(fd: number): void; } /** * Run `fn` holding a cross-process lock. A fully populated private claim directory * is atomically renamed to the canonical path, so contenders never observe partial * ownership metadata; its timestamp lets a crashed holder be reclaimed safely. * Same strategy as the runner's launch gate, factored out so both use one. * * The lock is always released, including when `fn` throws. */ export declare function withFileLock(lockPath: string, fn: () => T | Promise, deps?: LockDeps, staleMs?: number): Promise; /** * Replace a file's contents atomically: write a temp file in the SAME directory * (so the rename cannot cross a filesystem boundary), fsync it, then rename over * the target. A reader either sees the old file or the new one — never a * half-written one — and an interrupted write leaves the previous contents * intact. */ export declare function replaceFileAtomically(path: string, contents: string, mode?: number, deps?: WriteDeps): void;