/** * workspace-registry — write-set conflict detection + batch freeze + 3-point diff. * * G-026 M2 (ADR-0004 #13/#16/#22/#24): * - First release: conflict detection + batch freeze only * (ownership transfer / quarantine deferred). * - Registers write-set identities per task; overlapping/unowned writes * freeze admission (admissionFrozen = true). * - snapshot/diff at admission / execution-terminal / cleanup-confirmed: * classifies changed paths as owned / unowned / overlapping. */ import type { PathIdentity } from "./path-identity.js"; export type OwnedClassification = "owned" | "unowned" | "overlapping"; export interface RegistryEntry { taskId: string; attemptedAt: string; status: "admitted" | "running" | "done" | "cleanup-confirmed"; identityKeys: string[]; } export interface WorkspaceRegistry { /** Canonical identity key → write owner(s). One key normally has 1 writer. */ writes: Map; /** Canonical key → set of all tasks that ever claimed it (overlap detection). */ ownersByKey: Map>; /** Batch admission frozen until explicit recover releases it. */ admissionFrozen: boolean; /** Machine-readable reason when frozen. */ freezeReason?: string; frozenAt?: string; } export declare function createRegistry(): WorkspaceRegistry; /** * Assert a task's write set does not overlap already-registered writers. * Throws RegistryConflictError on overlap → caller must not admit in parallel. */ export declare class RegistryConflictError extends Error { readonly taskId: string; readonly conflictingKeys: string[]; constructor(taskId: string, conflictingKeys: string[]); } export declare function assertNoOverlap(registry: WorkspaceRegistry, taskId: string, identities: PathIdentity[]): void; export declare function registerWriteSet(registry: WorkspaceRegistry, taskId: string, identities: PathIdentity[], status?: RegistryEntry["status"], now?: string): void; /** * Freeze batch admission (fail-closed). Does NOT kill running tasks. * Only an explicit recover (operator/command) can clear it. */ export declare function freezeBatch(registry: WorkspaceRegistry, reason: string, now?: string): void; export interface WorkspaceSnapshot { /** repoBoundary → canonicalRelative set (upsert/added/modified/deleted by git) */ changes: Map>; recordedAt: string; } /** Snapshot the current git changes in a repo dir (porcelain status). */ export declare function collectGitChanges(repoDir: string, execAsyncImpl: (cmd: string, opts: { cwd: string; }) => Promise<{ stdout: string; }>): Promise>; /** Record snapshot under a point key (admission/execution-terminal/cleanup-confirmed). */ export declare function recordSnapshot(snapshots: Map, point: "admission" | "execution-terminal" | "cleanup-confirmed", changes: Set, repoMap: { workspace: string; submodules: { name: string; dir: string; }[]; }, now?: string): WorkspaceSnapshot; /** * Classify changed files at a later point against an earlier baseline. * A change is: * - owned: its boundary + relative path is inside a task's registered write set; * - overlapping: inside a write set that overlaps multiple tasks; * - unowned: not attributable to any registered writer (freeze batch). */ export declare function classifyDiff(baseline: WorkspaceSnapshot, current: WorkspaceSnapshot, registry: WorkspaceRegistry, taskIdUnderReview?: string): Map; /** * Collapse a write-set identity list to the canonical keys a diff classifier * will produce (boundary:). Used to register write sets that * line up with recordSnapshot/classifyDiff keys. */ export declare function identityKeysFor(identities: PathIdentity[]): string[];