/** * The user-level worktree registry (`~/.totem/worktrees.json`) behind * `totem wt create|remove|list` (mmnto-ai/totem#2580 slice-2). * * A SIBLING file to `registry.json`, never a key inside it: `RegistrySchema` is * `z.record(repoPath, RegistryEntrySchema)`, so any non-repo-entry key would * fail schema validation and flip the WHOLE sync registry to "unreadable". The * two files share one lock directory (`~/.totem`) and nothing else — the read * path of `registry.json` is untouched by every verb here. * * Two independent lifecycles live in this file, and the independence is the * point: * - `roots[]` accretes every container root a worktree was ever created * under and is NEVER pruned by removal. It is what keeps a recorded * location reachable for `doctor --estate` after the last live entry under * it is gone (PR #2586 round-3 MINOR-3). * - `worktrees{}` is entry accounting: written BEFORE `git worktree add` * (an intent record — a phantom entry fails VISIBLY in `wt list`, an * unrecorded worktree fails invisibly), deleted ONLY after a removal has * verified the directory is actually gone. * * Concurrency and durability copy `updateRegistryEntry` exactly: `acquireLock` * on `~/.totem` serializes mutations, and every write is a PID-suffixed temp * file renamed into place. Reads warn-and-degrade (`readRegistry`'s posture); * mutations REFUSE to overwrite a file whose schema does not parse. */ import { z } from 'zod'; /** The on-disk compatibility contract for `~/.totem/worktrees.json`. */ export declare const WORKTREE_REGISTRY_SCHEMA_VERSION = 1; export declare const WorktreeEntrySchema: z.ZodObject<{ /** Home repo root (absolute) the worktree was created from. */ repo: z.ZodString; /** Creating seat id (`resolveSelfSender`, or an explicit `--seat`). */ seat: z.ZodString; branch: z.ZodString; /** Issue number the worktree was cut for, when one was named. */ ticket: z.ZodOptional; /** ISO instant, stamped at the write site. */ createdAt: z.ZodString; }, "passthrough", z.ZodTypeAny, z.objectOutputType<{ /** Home repo root (absolute) the worktree was created from. */ repo: z.ZodString; /** Creating seat id (`resolveSelfSender`, or an explicit `--seat`). */ seat: z.ZodString; branch: z.ZodString; /** Issue number the worktree was cut for, when one was named. */ ticket: z.ZodOptional; /** ISO instant, stamped at the write site. */ createdAt: z.ZodString; }, z.ZodTypeAny, "passthrough">, z.objectInputType<{ /** Home repo root (absolute) the worktree was created from. */ repo: z.ZodString; /** Creating seat id (`resolveSelfSender`, or an explicit `--seat`). */ seat: z.ZodString; branch: z.ZodString; /** Issue number the worktree was cut for, when one was named. */ ticket: z.ZodOptional; /** ISO instant, stamped at the write site. */ createdAt: z.ZodString; }, z.ZodTypeAny, "passthrough">>; export declare const WorktreeFileSchema: z.ZodObject<{ schemaVersion: z.ZodLiteral<1>; /** Every container root ever created under — accretes, never auto-pruned. */ roots: z.ZodArray; /** Absolute worktree path → entry. */ worktrees: z.ZodRecord; /** ISO instant, stamped at the write site. */ createdAt: z.ZodString; }, "passthrough", z.ZodTypeAny, z.objectOutputType<{ /** Home repo root (absolute) the worktree was created from. */ repo: z.ZodString; /** Creating seat id (`resolveSelfSender`, or an explicit `--seat`). */ seat: z.ZodString; branch: z.ZodString; /** Issue number the worktree was cut for, when one was named. */ ticket: z.ZodOptional; /** ISO instant, stamped at the write site. */ createdAt: z.ZodString; }, z.ZodTypeAny, "passthrough">, z.objectInputType<{ /** Home repo root (absolute) the worktree was created from. */ repo: z.ZodString; /** Creating seat id (`resolveSelfSender`, or an explicit `--seat`). */ seat: z.ZodString; branch: z.ZodString; /** Issue number the worktree was cut for, when one was named. */ ticket: z.ZodOptional; /** ISO instant, stamped at the write site. */ createdAt: z.ZodString; }, z.ZodTypeAny, "passthrough">>>; }, "strip", z.ZodTypeAny, { schemaVersion: 1; worktrees: Record; /** ISO instant, stamped at the write site. */ createdAt: z.ZodString; }, z.ZodTypeAny, "passthrough">>; roots: string[]; }, { schemaVersion: 1; worktrees: Record; /** ISO instant, stamped at the write site. */ createdAt: z.ZodString; }, z.ZodTypeAny, "passthrough">>; roots: string[]; }>; export type WorktreeEntry = z.infer; export type WorktreeFile = z.infer; /** Resolve the registry file path lazily so tests can mock os.homedir(). */ export declare function worktreeRegistryPath(): string; /** The zero-config container root (`~/.totem/worktrees`) — the ruling's Q1 default. */ export declare function defaultWorktreeRoot(): string; /** A fresh, valid, empty registry — the first-run and degraded-read value. */ export declare function emptyWorktreeFile(): WorktreeFile; /** Case-folded (win32 only), resolved key for comparing two worktree paths. */ export declare function worktreePathKey(p: string): string; /** * No-follow existence probe: a dangling symlink/junction still EXISTS here, * and the probe FAILS CLOSED — only ENOENT/ENOTDIR mean "absent". Any other * errno (EACCES on a denied parent, EIO, an offline share) reports the path as * PRESENT: a caller that cannot KNOW must fail loud, never delete a registry * entry for a directory that may still stand (#2580 bot round, CR findings * 8 + 9 — this retires the errno-tri-state deferral). */ export declare function worktreePathExists(p: string): boolean; /** * Read the worktree registry. Missing file → empty (expected on first run); * any other failure warns and degrades to empty, mirroring `readRegistry` so a * corrupt file can never take a read-only verb down with it. */ export declare function readWorktreeRegistry(onWarn?: (msg: string) => void): WorktreeFile; /** * Find a recorded entry by path, folding case on win32 only. Returns the * STORED key alongside the entry so a caller can delete exactly what it found. */ export declare function findWorktreeEntry(file: WorktreeFile, worktreePath: string): { key: string; entry: WorktreeEntry; } | undefined; /** * The recorded roots that still EXIST on disk — the projection both doctor * consumers sweep. A recorded root that is gone is an empty sweep, not a scan * hole (the ruling's Q3), so it is filtered here rather than handed to * `scanEstate` as an unscannable row. */ export declare function existingWorktreeRoots(file: WorktreeFile): string[]; /** * Partition recorded roots into the sweep classes the estate consumers hand to * `scanEstate`. Only the DEFAULT root (`~/.totem/worktrees`) carries container * semantics — it exists solely to hold worktrees, so location alone is husk * evidence there. Every other recorded root is a location the operator also * uses for other things (a shared tmp, a scratch dir); those sweep as STANDARD * roots, where husk-ness needs shape evidence — so one `wt create --root` * against a scratch dir can never permanently arm by-location residue rows for * every unrelated directory in it (#2580 slice-2 falsification, finding 11). */ export declare function partitionWorktreeRoots(roots: string[]): { container: string[]; standard: string[]; }; /** * Record a worktree and the root it lives under, under the `~/.totem` lock. * Called BEFORE `git worktree add` — the intent record is what makes a failed * creation visible instead of invisible. */ export declare function addWorktreeEntry(args: { worktreePath: string; entry: WorktreeEntry; root: string; }): Promise; /** * Delete a recorded entry under the `~/.totem` lock. Roots are deliberately * untouched: their durability is independent of entry lifecycle, which is what * keeps an emptied container root reachable for the estate sweep. * * Callers must only reach this AFTER verifying the directory is absent. * Returns whether an entry was actually deleted (a git-listed worktree with no * registry entry — the legacy estate — is a no-op, not an error). * * `expectCreatedAt` is an identity guard for the verify→delete window: a * concurrent `wt create` can re-record the SAME path between a removal's * absence check and this lock acquisition, and an unguarded delete-by-path * would erase the replacement's durable record — the invisible-unrecorded * class this registry exists to prevent (#2580 bot round, Greptile P1). When * the stored entry's `createdAt` differs from the expected one, the entry is * left in place and `false` is returned. */ export declare function deleteWorktreeEntry(worktreePath: string, opts?: { expectCreatedAt?: string; }): Promise; //# sourceMappingURL=worktree-registry.d.ts.map