/** * #2661 — Cross-worktree AI job dedup (issue invariant 5). * * N worktrees of one repository checked out at the same HEAD schedule the * same analyses independently: N audits of identical content, N optimize * passes, N testgap sweeps. Before a model launch, callers compute * * jobKey = sha256(repositoryId, head, workerType, workerConfigHash) * * and skip the launch when the same job succeeded within the freshness * window. HEAD moves → new key → the job runs again. * * The registry lives next to the AI budget ledger under the user's home * directory (owner-only, symlink-rejecting) so all daemons share it: * * ~/.claude-flow/ai-jobs.json * * This is a best-effort OPTIMIZATION layered under the budget: two daemons * racing the same key may both miss and both attempt a launch, but the * budget's atomic reservation (maxConcurrentGlobal / hourly cap) is the hard * invariant that bounds actual launches. Only operational metadata is * persisted — never prompts, outputs, or source content. */ export interface AiJobKeyParts { repositoryId: string; head: string; workerType: string; /** Hash of the effective worker config (prompt/model/sandbox/patterns). */ configHash: string; } export declare function computeAiJobKey(parts: AiJobKeyParts): string; /** Stable hash of an arbitrary config object (key-sorted JSON). */ export declare function hashWorkerConfig(config: unknown): string; export declare class AiJobDedupRegistry { private readonly dir; private readonly file; constructor(options?: { baseDir?: string; }); /** * True when the job succeeded within `freshnessMs`. Any registry error * reads as "not fresh" — dedup failing open only costs a (budget-capped) * launch, never correctness. */ isFresh(jobKey: string, freshnessMs: number): { fresh: boolean; lastRunAt?: number; }; /** Record a successful run of a job. Best-effort. */ recordSuccess(jobKey: string, meta: { workerType: string; repositoryId: string; workspace: string; }): void; private read; private write; } export declare function getAiJobDedupRegistry(): AiJobDedupRegistry; /** Test hook: reset the singleton (e.g. after changing RUFLO_AI_BUDGET_DIR). */ export declare function resetAiJobDedupRegistryForTests(): void; //# sourceMappingURL=ai-job-dedup.d.ts.map