/** * ADR-406 M0 — durable mission storage. * * Store decision (audit in the PR): ruflo's authoritative state already lives * in project files guarded by a lock and replaced atomically (the policy * ledger in `.claude-flow/policy/`, claims, workspace leases). `better-sqlite3` * is an optional dependency the CLI must run without, and `sql.js` keeps the * database in memory and persists by whole-file export, which is neither * append-durable nor safe across processes. So a mission is: * * .claude-flow/missions//events.jsonl append-only, hash-chained * .claude-flow/missions//snapshot.json materialized cache (rebuildable) * .claude-flow/missions//lock per-mission writer lock * * The log is the truth; the snapshot is only a cache that must agree with it. * Each event is one write of one line followed by fsync. A crash can leave at * most an unterminated last line, which readers ignore and the next locked * writer truncates. Writers take the policy runtime's lock (`acquireLock`), * which recognises a dead owner by pid, pid namespace and boot id. */ import { type MissionEvent } from './schemas.js'; import type { MissionView } from './transitions.js'; export declare const MISSIONS_DIR: string; export interface LogRead { readonly events: MissionEvent[]; /** Bytes covered by complete lines; anything after is a torn tail. */ readonly validBytes: number; readonly tornTail: boolean; } export declare class MissionStoreError extends Error { readonly code: string; constructor(code: string, message: string); } /** tmp + fsync + rename + directory fsync. */ export declare function writeFileAtomic(path: string, text: string, mode?: number): void; export declare function parseLog(buffer: Buffer): LogRead; export declare class MissionStore { readonly root: string; constructor(projectRoot: string); missionDir(missionId: string): string; private logPath; private snapshotPath; exists(missionId: string): boolean; listMissionIds(): string[]; /** Atomic claim of a new mission id: `mkdir` fails if another request won. */ claim(missionId: string): boolean; readLog(missionId: string): LogRead; /** Snapshot when it agrees with the log, then fold the tail; otherwise full replay. */ loadView(missionId: string, log?: LogRead): MissionView | null; /** * Run `fn` under the mission's writer lock with a freshly replayed view. * Events `fn` returns are appended (each fsynced) only after `fn` validated * them, then the snapshot is refreshed. */ withMission(missionId: string, fn: (view: MissionView | null, log: LogRead) => { events: MissionEvent[]; result: T; view: MissionView | null; }): Promise; private append; writeSnapshot(missionId: string, view: MissionView): void; /** For diagnostics and tests: the log file's size in bytes. */ logSize(missionId: string): number; /** Workspace-level lock for the observation file. */ withWorkspaceLock(fn: () => T): Promise; writeWorkspaceFile(name: string, text: string): void; } //# sourceMappingURL=store.d.ts.map