/**
 * Resolve the configured target without permitting project-root escape, and
 * without permitting an auto-loaded rules tree.
 *
 * The eager-tree arm is the write boundary's own guard, deliberately duplicated
 * from `learnings.file` config validation rather than delegated to it. Lisa
 * 2.232.0 scaffolded the ledger into `.claude/rules/` and every downstream
 * project happily wrote there: the raw ledger entered eager context in every
 * session, and a merge-hostile file sat where nothing watched it until 19
 * captured learnings vanished in a silent merge. Every writer — ledger,
 * overflow, apply's relocation — funnels through this function, so failing
 * loudly HERE means no future caller can reintroduce that placement by
 * synthesizing a path that never passed through config validation.
 * @param projectRoot - Host project root
 * @param relativeFile - Configured project-relative file
 * @returns Resolved root and target paths
 */
export declare function resolveSafeLearningTarget(projectRoot: string, relativeFile: string): {
    readonly root: string;
    readonly target: string;
};
/**
 * Read only a bounded regular file from the inode that was safety-checked.
 * @param target - Resolved learnings file path
 * @returns Existing content, or undefined when the file does not exist
 */
export declare function readExistingLearnings(target: string): Promise<string | undefined>;
/** A locked ledger transaction found its read-image already overwritten. */
export declare class LearningsConcurrentWriteError extends Error {
    /**
     * Build the operator-readable concurrent-write diagnosis.
     * @param target - Resolved learnings file path
     */
    constructor(target: string);
}
/**
 * Prove the ledger still holds the bytes this transaction read before letting
 * an atomic rename publish over them.
 *
 * The ledger is read-modify-write, so a writer that publishes over an image it
 * did not read erases whatever landed in between. The lock is what prevents
 * that, and this is the arm that makes a lock failure LOUD: the whole class of
 * bugs here (CodySwannGT/lisa#2488, and the 19 entries lost to a merge before
 * it) is defined by nothing noticing. This detects an interloper rather than
 * excluding one — it is the second line, not the lock — but it converts a
 * silent lost learning into a failed capture the caller can retry.
 * @param target - Resolved learnings file path
 * @param preImage - Content this transaction read under the lock
 */
export declare function assertLearningsUnchanged(target: string, preImage: string | undefined): Promise<void>;
/**
 * Reject parent-directory symlinks that resolve outside the project root.
 * @param root - Resolved project root
 * @param parent - Target parent directory
 */
export declare function assertSafeLearningParents(root: string, parent: string): Promise<void>;
//# sourceMappingURL=learnings-file-safety.d.ts.map