/** * Pool Storage — JSON file persistence for Task Pool * * Handles reading and writing the task pool state to disk using * atomic writes (temp-file + fsync + rename) and operation locks * to prevent corruption on crash or concurrent access. * * @module services/task-pool/pool-storage */ import type { WorkItem } from '../../types/v2/work-item.types.js'; import type { TaskClaim } from '../../types/v2/claim.types.js'; /** * On-disk shape of the pool data file. */ export interface PoolData { /** All work items currently in the pool */ workItems: WorkItem[]; /** All active and historical claims */ claims: TaskClaim[]; /** ISO8601 — last time the file was written */ lastUpdatedAt: string; } /** * Options for PoolStorage construction. */ export interface PoolStorageOptions { /** Override the default data directory (defaults to ~/.crewly/task-pool/) */ dataDir?: string; } /** * Persists task pool state to a JSON file with atomic writes. * * Uses the same patterns as the rest of the Crewly backend: * - {@link atomicWriteFile} for crash-safe writes * - {@link modifyJsonFile} for locked read-modify-write cycles * - Debounced flushing to reduce disk I/O under burst writes */ export declare class PoolStorage { private readonly filePath; private readonly logger; /** In-memory cache — authoritative after first load. */ private cache; /** Debounce timer handle for batching writes. */ private flushTimer; /** Whether a flush is pending. */ private dirty; constructor(options?: PoolStorageOptions); /** * Loads pool data from disk (or returns cached copy). * * **F-CYCLE7-3 (2026-05-07):** Normalizes legacy / partial on-disk * shapes — if a previous write produced a `pool.json` missing the * `workItems` or `claims` arrays (root cause of the post-restart * `Cannot read properties of undefined (reading 'filter')` storm in * `ReconcilerDataProvider`), we coerce the missing slots to `[]` * before caching so every downstream consumer can rely on the * `PoolData` invariant. * * @returns Current pool data with `workItems` and `claims` guaranteed to be arrays */ load(): Promise; /** * Returns all work items currently in the pool. * * Always returns an array — `load()` normalizes a missing * `workItems` field to `[]` (F-CYCLE7-3). * * @returns Array of WorkItems */ getWorkItems(): Promise; /** * Returns all claims. * * Always returns an array — `load()` normalizes a missing `claims` * field to `[]` (F-CYCLE7-3). * * @returns Array of TaskClaims */ getClaims(): Promise; /** * Finds a work item by ID. * * @param workItemId - The work item ID * @returns The work item, or undefined */ findWorkItem(workItemId: string): Promise; /** * Finds a claim by work item ID (active claims only). * * @param workItemId - The work item ID * @returns The active claim, or undefined */ findActiveClaimByWorkItem(workItemId: string): Promise; /** * Finds a claim by agent ID (active claims only). * * @param agentId - The agent session name * @returns The active claim, or undefined */ findActiveClaimByAgent(agentId: string): Promise; /** * Adds a work item to the pool. * * @param workItem - The work item to add */ addWorkItem(workItem: WorkItem): Promise; /** * Updates a work item in-place. * * @param workItemId - ID of the work item to update * @param updater - Function that mutates the work item * @returns True if item was found and updated */ updateWorkItem(workItemId: string, updater: (wi: WorkItem) => void): Promise; /** * Removes a work item from the pool. * * @param workItemId - ID of the work item to remove * @returns True if item was found and removed */ removeWorkItem(workItemId: string): Promise; /** * Removes several work items at once (one in-memory pass, one flush * scheduled). Used by the one-time archive, which writes the items to the * archive file first. * * @param workItemIds - IDs to remove * @returns How many were removed */ removeWorkItems(workItemIds: ReadonlySet): Promise; /** * Removes several claims at once (in place, one flush scheduled). Used by * the pool prune, which writes them to the archive first. * * @param claimIds - IDs to remove * @returns How many were removed */ removeClaims(claimIds: ReadonlySet): Promise; /** * Directory the pool file lives in (`~/.crewly/task-pool` by default). * * @returns Absolute directory path */ getDataDir(): string; /** * Adds a claim to storage. * * @param claim - The claim to add */ addClaim(claim: TaskClaim): Promise; /** * Updates a claim in-place. * * @param claimId - ID of the claim to update * @param updater - Function that mutates the claim * @returns True if claim was found and updated */ updateClaim(claimId: string, updater: (claim: TaskClaim) => void): Promise; /** * Forces an immediate flush to disk (bypasses debounce). * Call this during graceful shutdown. */ flush(): Promise; /** * Clears all pool data (useful for testing). */ clear(): Promise; /** * Cleanup: cancels pending debounce timer and flushes. */ destroy(): Promise; /** * Marks cache as dirty and schedules a debounced flush. */ private markDirty; } /** * Creates an empty pool data structure. * * @returns Empty PoolData */ export declare function createEmptyPoolData(): PoolData; //# sourceMappingURL=pool-storage.d.ts.map