/** * Project-level continuity index writer with serial queue. * * Manages the `project-continuity.json` file at the session-tracker root. * Connects all main sessions across the project for cross-session navigation. * Uses a serial promise queue to prevent concurrent write corruption (REQ-ST-09). * * File location: `.hivemind/session-tracker/project-continuity.json` * * @module session-tracker/persistence/project-index-writer */ import type { ProjectSessionEntry } from "../types.js"; import type { OpenCodeClient } from "../../../shared/session-api.js"; import type { HierarchyIndex } from "./hierarchy-index.js"; /** * Manages the project-level continuity index with serialized concurrent writes. * * All mutation methods are serialized through `writeQueue` to ensure * only one write is in-flight at a time. This prevents corruption * when up to 6 concurrent sessions write to the same index file. */ export declare class ProjectIndexWriter { private client; private projectRoot; /** * Optional hierarchy index for computing childCount and totalDelegationDepth. * When undefined, these fields will remain at their default values (0). */ private hierarchyIndex; /** * Timestamp of the last successful write (epoch ms). * Initialized to now so the queue is immediately "healthy." */ private lastWriteTime; /** * Duration of write inactivity before the queue is detected as stale * and auto-recovered (5 minutes). */ private static readonly STALE_QUEUE_MS; /** * Promise-based serial queue. Each write chains after the previous one. * Initialized to a resolved promise to allow the first write to proceed. */ private writeQueue; /** * @param deps - Injected dependencies. * @param deps.client - The OpenCode SDK client for logging. * @param deps.projectRoot - Absolute path to the project root. * @param deps.hierarchyIndex - Optional hierarchy index for childCount/depth. */ constructor(deps: { client: OpenCodeClient; projectRoot: string; hierarchyIndex?: HierarchyIndex; }); /** * Returns the absolute path to the project-continuity.json file. * * @returns Absolute file path. */ private getIndexPath; /** * Reads the existing project index or returns a default. * * @returns The parsed index (or a new default if the file doesn't exist). */ private readIndex; /** * Creates a default project continuity index. * * @returns A fresh default index. */ private createDefault; /** * Initializes the project-level continuity index file. * * Creates the session-tracker root directory and writes the default * index atomically. Uses the serial queue to prevent concurrent write * corruption with hook-triggered writes. * * Only writes if the file does not already exist — preserves any * lazily-bootstrapped session entries written before initialization * completes. * * @returns Promise that resolves when the index is written. */ initializeIndex(): Promise; /** * Removes stale entries from the project index whose directories * no longer exist on disk. * * This prevents the respawn cycle: tests write entries to * project-continuity.json, cleanup removes directories but not entries, * then initialize() reads stale entries and recreates directories. * * Best-effort: individual failures are silently skipped. * * @returns Number of stale entries removed. */ cleanupStaleEntries(): Promise; /** * Adds a new main session to the project index. * * Serialized via the write queue to prevent concurrent write corruption. * * @param sessionID - The session identifier. * @param sessionDir - Relative path to the session subdirectory. * @param mainFile - Filename of the main session `.md` file. * @returns Promise that resolves when the index is updated. */ addSession(sessionID: string, sessionDir: string, mainFile: string): Promise; /** * Updates an existing session's metadata in the project index. * * Serialized via the write queue. Merges partial updates into the * existing session entry. * * NOTE: The `childCount` and `totalDelegationDepth` fields in the * `updates` parameter are ALWAYS overridden from the hierarchy index * (see F-19 lines below). Caller-supplied values for these fields are * silently ignored — they are computed from the canonical hierarchy * index to prevent drift between the two stores. * * @param sessionID - The session identifier. * @param updates - Partial session metadata to merge. `childCount` and * `totalDelegationDepth` are always sourced from hierarchy index. * @returns Promise that resolves when the index is updated. */ updateSession(sessionID: string, updates: Partial): Promise; /** * Computes childCount from the hierarchy index. * Returns 0 if no hierarchyIndex is wired (graceful degradation). * * @param sessionID - The session to count children for. * @returns The number of direct children. */ private computeChildCount; /** * Computes the maximum delegation depth from the hierarchy index. * Returns 0 if no hierarchyIndex is wired. * * @param sessionID - The session to measure depth for. * @returns The maximum delegation depth. */ private computeMaxDepth; /** * Atomically increments the childCount for a session in the project index. * * If `depth` is provided and exceeds the current `totalDelegationDepth`, * updates it to reflect the deepest delegation seen (AC-10). * * Serialized via the write queue to prevent concurrent update corruption. * * @param sessionID - The parent session identifier. * @param depth - Optional delegation depth of the new child. * @returns Promise that resolves when the index is updated. */ incrementChildCount(sessionID: string, depth?: number): Promise; /** * Removes a session from the project index. * * Serialized via the write queue. Removes the session entry and * updates the chronological order. * * @param sessionID - The session identifier to remove. * @returns Promise that resolves when the index is updated. */ removeSession(sessionID: string): Promise; /** * Checks if the write queue has been idle beyond the stale threshold. * * If `lastWriteTime` is older than `STALE_QUEUE_MS`, logs a warning * and resets the queue so subsequent writes are not blocked by a stuck * preceding promise (DEFECT-02). */ private detectStaleQueue; /** * Returns the current health of the serial write queue. * * @returns Object with `lastWriteTime` as an ISO string and `stalled` * boolean indicating whether the queue has exceeded the stale threshold. */ getQueueHealth(): Promise<{ lastWriteTime: string; stalled: boolean; }>; /** * Enqueues a write operation into the serial queue. * * Stale-queue detection runs FIRST to auto-recover from a frozen pipeline. * Chains the provided function onto the end of `writeQueue` so that only * one write is in-flight at a time. Records `lastWriteTime` on success. * Errors are caught and logged to prevent a failed write from breaking the * queue entirely. A final `.then()` ensures the promise chain always resolves * to void (DEFECT-02). * * @param fn - The write operation to enqueue. * @returns Promise that resolves when the enqueued write completes. */ private enqueueWrite; } //# sourceMappingURL=project-index-writer.d.ts.map