@cleocode/worktree 2026.9.20 → 2026.9.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,12 +10,13 @@
10
10
  * @adr ADR-062
11
11
  */
12
12
  import { existsSync, rmSync } from 'node:fs';
13
- import { getGitRoot, gitSilent, gitSync } from './git.js';
13
+ import { countUnmergedCommits, getGitRoot, gitSilent, gitSync } from './git.js';
14
14
  import { destroyWorktree as napiDestroyWorktree } from './napi-binding.js';
15
15
  import { computeProjectHash, resolveTaskWorktreePath } from './paths.js';
16
16
  import { appendWorktreeAuditLog, removeWorktreeFromSentinelIndex } from './worktree-audit.js';
17
17
  import { runWorktreeHooks } from './worktree-hooks.js';
18
18
  import { locateTaskWorktree } from './worktree-locate.js';
19
+ import { releaseWorktreeTaskLock } from './worktree-lock.js';
19
20
  /**
20
21
  * Destroy the git worktree for a task.
21
22
  *
@@ -151,11 +152,23 @@ export async function destroyWorktree(projectRoot, options) {
151
152
  else {
152
153
  worktreeRemoved = true; // genuinely absent: not on disk, not registered
153
154
  }
155
+ // T12506: the worktree is gone, so its per-task lock guards nothing — free
156
+ // it so the next spawn of this task provisions without waiting for a TTL.
157
+ if (worktreeRemoved)
158
+ releaseWorktreeTaskLock(projectHash, taskId);
154
159
  // Step 4: Optionally delete the branch.
155
160
  if (deleteBranch) {
156
161
  try {
157
162
  const branchExists = gitSync(['branch', '--list', branch], gitRoot);
158
- if (branchExists) {
163
+ const unmerged = branchExists ? countUnmergedCommits(gitRoot, branch, ['HEAD']) : 0;
164
+ if (branchExists && unmerged > 0 && options.forceDeleteUnmergedBranch !== true) {
165
+ // T12506: never delete history that exists nowhere else implicitly.
166
+ branchDeleted = false;
167
+ if (!error) {
168
+ error = `Branch '${branch}' kept: ${unmerged} commit(s) are on neither origin/main nor HEAD. Merge it, or pass forceDeleteUnmergedBranch to delete it anyway.`;
169
+ }
170
+ }
171
+ else if (branchExists) {
159
172
  gitSync(['branch', '-D', branch], gitRoot);
160
173
  branchDeleted = true;
161
174
  }
@@ -1 +1 @@
1
- {"version":3,"file":"worktree-destroy.js","sourceRoot":"","sources":["../src/worktree-destroy.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"worktree-destroy.js","sourceRoot":"","sources":["../src/worktree-destroy.ts"],"names":[],"mappings":"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"}
@@ -3,7 +3,8 @@
3
3
  * feature per D030.
4
4
  *
5
5
  * Executes `post-create` and `post-start` hooks in the worktree directory.
6
- * Each hook runs via `sh -c <command>` with the worktree path as CWD.
6
+ * Each hook runs through the platform shell ({@link shellInvocation}: `/bin/sh -c`
7
+ * on POSIX, cmd.exe on Windows) with the worktree path as CWD (T12604).
7
8
  *
8
9
  * @task T1161
9
10
  */
@@ -1 +1 @@
1
- {"version":3,"file":"worktree-hooks.d.ts","sourceRoot":"","sources":["../src/worktree-hooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAI5E;;;;;;GAMG;AACH,wBAAsB,aAAa,CACjC,IAAI,EAAE,YAAY,EAClB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,kBAAkB,CAAC,CA6B7B;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,SAAS,YAAY,EAAE,EAC9B,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,EAC5B,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAiB/B"}
1
+ {"version":3,"file":"worktree-hooks.d.ts","sourceRoot":"","sources":["../src/worktree-hooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAK5E;;;;;;GAMG;AACH,wBAAsB,aAAa,CACjC,IAAI,EAAE,YAAY,EAClB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,kBAAkB,CAAC,CAgC7B;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,SAAS,YAAY,EAAE,EAC9B,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,EAC5B,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAiB/B"}
@@ -3,11 +3,13 @@
3
3
  * feature per D030.
4
4
  *
5
5
  * Executes `post-create` and `post-start` hooks in the worktree directory.
6
- * Each hook runs via `sh -c <command>` with the worktree path as CWD.
6
+ * Each hook runs through the platform shell ({@link shellInvocation}: `/bin/sh -c`
7
+ * on POSIX, cmd.exe on Windows) with the worktree path as CWD (T12604).
7
8
  *
8
9
  * @task T1161
9
10
  */
10
11
  import { execFile } from 'node:child_process';
12
+ import { shellInvocation } from '@cleocode/paths';
11
13
  const DEFAULT_HOOK_TIMEOUT_MS = 30_000;
12
14
  /**
13
15
  * Execute a single declarative hook in the given worktree directory.
@@ -19,11 +21,13 @@ const DEFAULT_HOOK_TIMEOUT_MS = 30_000;
19
21
  export async function runSingleHook(hook, worktreePath) {
20
22
  const timeoutMs = hook.timeoutMs ?? DEFAULT_HOOK_TIMEOUT_MS;
21
23
  const start = Date.now();
24
+ const shell = shellInvocation(hook.command);
22
25
  return new Promise((resolve) => {
23
- const child = execFile('sh', ['-c', hook.command], {
26
+ const child = execFile(shell.file, shell.args, {
24
27
  cwd: worktreePath,
25
28
  timeout: timeoutMs,
26
29
  encoding: 'utf-8',
30
+ windowsVerbatimArguments: shell.windowsVerbatimArguments,
27
31
  }, (_err, stdout, stderr) => {
28
32
  const durationMs = Date.now() - start;
29
33
  const exitCode = child.exitCode;
@@ -1 +1 @@
1
- {"version":3,"file":"worktree-hooks.js","sourceRoot":"","sources":["../src/worktree-hooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAG9C,MAAM,uBAAuB,GAAG,MAAM,CAAC;AAEvC;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,IAAkB,EAClB,YAAoB;IAEpB,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,uBAAuB,CAAC;IAC5D,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAEzB,OAAO,IAAI,OAAO,CAAqB,CAAC,OAAO,EAAE,EAAE;QACjD,MAAM,KAAK,GAAG,QAAQ,CACpB,IAAI,EACJ,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,EACpB;YACE,GAAG,EAAE,YAAY;YACjB,OAAO,EAAE,SAAS;YAClB,QAAQ,EAAE,OAAO;SAClB,EACD,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;YACvB,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;YACtC,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;YAChC,MAAM,QAAQ,GAAG,QAAQ,KAAK,IAAI,IAAI,UAAU,IAAI,SAAS,CAAC;YAE9D,OAAO,CAAC;gBACN,IAAI;gBACJ,OAAO,EAAE,QAAQ,KAAK,CAAC;gBACvB,MAAM,EAAE,CAAC,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE;gBACzD,MAAM,EAAE,CAAC,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE;gBACzD,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC;gBAC3C,UAAU;aACX,CAAC,CAAC;QACL,CAAC,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,KAA8B,EAC9B,KAA4B,EAC5B,YAAoB;IAEpB,MAAM,OAAO,GAAyB,EAAE,CAAC;IACzC,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC;IAExD,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;QACvD,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAErB,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACxC,MAAM,IAAI,KAAK,CACb,4CAA4C,IAAI,CAAC,OAAO,IAAI;gBAC1D,SAAS,MAAM,CAAC,QAAQ,aAAa,MAAM,CAAC,MAAM,EAAE,CACvD,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
1
+ {"version":3,"file":"worktree-hooks.js","sourceRoot":"","sources":["../src/worktree-hooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD,MAAM,uBAAuB,GAAG,MAAM,CAAC;AAEvC;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,IAAkB,EAClB,YAAoB;IAEpB,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,uBAAuB,CAAC;IAC5D,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAEzB,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAE5C,OAAO,IAAI,OAAO,CAAqB,CAAC,OAAO,EAAE,EAAE;QACjD,MAAM,KAAK,GAAG,QAAQ,CACpB,KAAK,CAAC,IAAI,EACV,KAAK,CAAC,IAAI,EACV;YACE,GAAG,EAAE,YAAY;YACjB,OAAO,EAAE,SAAS;YAClB,QAAQ,EAAE,OAAO;YACjB,wBAAwB,EAAE,KAAK,CAAC,wBAAwB;SACzD,EACD,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;YACvB,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;YACtC,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;YAChC,MAAM,QAAQ,GAAG,QAAQ,KAAK,IAAI,IAAI,UAAU,IAAI,SAAS,CAAC;YAE9D,OAAO,CAAC;gBACN,IAAI;gBACJ,OAAO,EAAE,QAAQ,KAAK,CAAC;gBACvB,MAAM,EAAE,CAAC,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE;gBACzD,MAAM,EAAE,CAAC,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE;gBACzD,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC;gBAC3C,UAAU;aACX,CAAC,CAAC;QACL,CAAC,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,KAA8B,EAC9B,KAA4B,EAC5B,YAAoB;IAEpB,MAAM,OAAO,GAAyB,EAAE,CAAC;IACzC,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC;IAExD,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;QACvD,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAErB,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACxC,MAAM,IAAI,KAAK,CACb,4CAA4C,IAAI,CAAC,OAAO,IAAI;gBAC1D,SAAS,MAAM,CAAC,QAAQ,aAAa,MAAM,CAAC,MAAM,EAAE,CACvD,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,211 @@
1
+ /**
2
+ * Per-task worktree lock (T12506 · epic T12498 E-WORKTREE-MULTI-AGENT).
3
+ *
4
+ * `createWorktree` used to be check-then-act: when the task's worktree
5
+ * directory existed and `git status --porcelain` was empty it force-removed the
6
+ * worktree and ran `git branch -D task/<id>`. A LIVE agent's worktree looks
7
+ * clean whenever its only changes are gitignored (`.cleo/cleo.db`) or already
8
+ * committed, so a second `cleo orchestrate spawn <id>` deleted files out from
9
+ * under the running agent.
10
+ *
11
+ * This module is the mutual-exclusion half of the fix. Before any
12
+ * `git worktree add`, the spawner takes an atomic per-task lock:
13
+ *
14
+ * - **Location:** `<cleoHome>/locks/worktrees/<projectHash>/<taskId>.lock`
15
+ * (via `@cleocode/paths`), shared by every spawn of the task on this machine.
16
+ * - **Atomic publish:** the record is written to a private temp file and then
17
+ * hard-linked into place. `link(2)` fails with `EEXIST` when the lock already
18
+ * exists, so exactly one contender wins and a reader never sees a partially
19
+ * written record. Filesystems without hard links (exFAT, some network
20
+ * mounts) fall back to `open(O_CREAT|O_EXCL)` on the final path; a reader
21
+ * that meets a young, not-yet-parseable record waits instead of reclaiming.
22
+ * - **Every acquisition failure is a refusal.** An I/O error (EACCES, EROFS,
23
+ * ENOSPC, ...) surfaces as `E_WORKTREE_LOCKED` with `reason:
24
+ * 'lock-unavailable'` — never as a generic error a caller might answer with
25
+ * worktree cleanup.
26
+ * - **Holder identity:** session id, agent id, device id, owner pid plus the
27
+ * owner's `ps lstart` start time (rendered under `PS_STABLE_ENV`), host name
28
+ * and a heartbeat timestamp.
29
+ * - **Liveness:** a held lock is reclaimable only when its holder is provably
30
+ * dead (pid gone, or alive with a different start time = recycled pid). The
31
+ * heartbeat TTL applies ONLY to holders whose pid cannot be verified (another
32
+ * device, or no recorded start time); a verified live holder is never
33
+ * reclaimed on age. Anything else is `E_WORKTREE_LOCKED`.
34
+ * - **Re-entry:** the same caller (session id, agent id, owner pid and start
35
+ * time all equal) re-enters its own lock, so an orchestrator can retry.
36
+ * - **Reclaim:** serialised through a second O_EXCL "reclaim mutex" file so two
37
+ * contenders that both saw the same dead holder cannot both win; the winner
38
+ * re-reads the lock and only replaces it if it is still the record it judged.
39
+ *
40
+ * @module worktree-lock
41
+ * @task T12506
42
+ */
43
+ import type { WorktreeLockAcquisition, WorktreeLockHolder, WorktreeLockRecord } from '@cleocode/contracts';
44
+ import { BRANCH_LOCK_ERROR_CODES } from '@cleocode/contracts';
45
+ /** Default heartbeat TTL: 4 hours. Override with `CLEO_WORKTREE_LOCK_TTL_MS`. */
46
+ export declare const DEFAULT_WORKTREE_LOCK_TTL_MS: number;
47
+ /** Environment variable overriding {@link DEFAULT_WORKTREE_LOCK_TTL_MS}. */
48
+ export declare const WORKTREE_LOCK_TTL_ENV = "CLEO_WORKTREE_LOCK_TTL_MS";
49
+ /** Liveness snapshot of one process. */
50
+ export interface ProcessLiveness {
51
+ /** True when the pid exists (signal 0 succeeded or was refused with EPERM). */
52
+ alive: boolean;
53
+ /** `ps -o lstart=` under {@link PS_STABLE_ENV}, or `null` when unprobeable. */
54
+ startedAt: string | null;
55
+ }
56
+ /** Reads the liveness of a pid; tests inject fakes. */
57
+ export type ProcessProbe = (pid: number) => ProcessLiveness;
58
+ /** Verdict on an existing lock record. */
59
+ export interface WorktreeLockAssessment {
60
+ /** True when the holder must be treated as live (lock not reclaimable). */
61
+ live: boolean;
62
+ /** Why a non-live holder is reclaimable. */
63
+ reason?: 'pid-gone' | 'pid-recycled' | 'heartbeat-stale';
64
+ /** True when the holder's pid + start time were verified live on this device. */
65
+ verified?: boolean;
66
+ }
67
+ /** Options for {@link acquireWorktreeTaskLock}. */
68
+ export interface AcquireWorktreeTaskLockOptions {
69
+ /** Project hash scoping the worktree. */
70
+ projectHash: string;
71
+ /** Task the lock guards. */
72
+ taskId: string;
73
+ /** Holder identity; missing pid/start default to the calling process. */
74
+ holder?: WorktreeLockHolder;
75
+ /** Heartbeat TTL in ms (default {@link resolveWorktreeLockTtlMs}). */
76
+ ttlMs?: number;
77
+ /** Process probe (default {@link probeProcess}). */
78
+ probe?: ProcessProbe;
79
+ /** Clock (default `Date.now`). */
80
+ now?: () => number;
81
+ /** This device's id (default: the persisted `<cleoHome>/device-id`, if any). */
82
+ deviceId?: string | null;
83
+ }
84
+ /** Error thrown when the lock is held by a live holder. */
85
+ export interface WorktreeLockedError extends Error {
86
+ /** Always `E_WORKTREE_LOCKED`. */
87
+ code: typeof BRANCH_LOCK_ERROR_CODES.E_WORKTREE_LOCKED;
88
+ /** Numeric exit code (`ExitCode.WORKTREE_LOCKED`). */
89
+ exitCode: number;
90
+ /** The live holder's record; `null` when the lock itself could not be taken. */
91
+ holder: WorktreeLockRecord | null;
92
+ /** `held` = a live holder owns it; `lock-unavailable` = the lock file could not be created or read. */
93
+ reason: 'held' | 'lock-unavailable';
94
+ /** Absolute path of the lock file. */
95
+ lockPath: string;
96
+ /** Remediation hint. */
97
+ fix: string;
98
+ }
99
+ /**
100
+ * Resolve the heartbeat TTL: a positive integer `CLEO_WORKTREE_LOCK_TTL_MS`
101
+ * wins, anything else falls back to {@link DEFAULT_WORKTREE_LOCK_TTL_MS}.
102
+ *
103
+ * @param env - Environment to read (defaults to `process.env`).
104
+ * @returns TTL in milliseconds.
105
+ */
106
+ export declare function resolveWorktreeLockTtlMs(env?: NodeJS.ProcessEnv): number;
107
+ /**
108
+ * Read a process's start time via `ps -o lstart=` with the stable env
109
+ * (UTC, C locale). Returns `null` on Windows or when `ps` fails.
110
+ *
111
+ * @param pid - Process id.
112
+ * @returns Whitespace-normalised `lstart` string, or `null`.
113
+ */
114
+ export declare function readProcessStartTime(pid: number): string | null;
115
+ /**
116
+ * Probe whether a pid is alive and, when it is, read its start time.
117
+ *
118
+ * `process.kill(pid, 0)` delivers no signal: `ESRCH` means no such process,
119
+ * `EPERM` means it exists but belongs to another user (still alive).
120
+ *
121
+ * @param pid - Process id.
122
+ * @returns Liveness snapshot.
123
+ */
124
+ export declare function probeProcess(pid: number): ProcessLiveness;
125
+ /**
126
+ * Read this device's persisted stable id (`<cleoHome>/device-id`), or `null`
127
+ * when core has not created it yet. Never creates it.
128
+ *
129
+ * @returns The device id, or `null`.
130
+ */
131
+ export declare function readStableDeviceId(): string | null;
132
+ /**
133
+ * Decide whether an existing lock's holder is live.
134
+ *
135
+ * - Same device (device ids equal; host names equal when either id is
136
+ * unknown): the pid is probed. Gone → `pid-gone`; alive with a different
137
+ * start time → `pid-recycled`; alive with the SAME start time → live and
138
+ * verified, whatever the heartbeat age (a verified live holder is never
139
+ * reclaimed on age — T12506 review).
140
+ * - Unverifiable (another device, or no start time on either side): only the
141
+ * heartbeat TTL can release it.
142
+ *
143
+ * @param record - The lock record on disk.
144
+ * @param opts - TTL, clock, probe and this device's id.
145
+ * @returns The assessment.
146
+ */
147
+ export declare function assessWorktreeLockHolder(record: WorktreeLockRecord, opts: {
148
+ ttlMs: number;
149
+ now: number;
150
+ probe: ProcessProbe;
151
+ deviceId: string | null;
152
+ }): WorktreeLockAssessment;
153
+ /**
154
+ * True when `candidate` is the same caller as the lock's `holder`: session id
155
+ * and agent id both present and equal, and the same owner process (pid and
156
+ * start time). Only then may a spawn re-enter an existing lock.
157
+ *
158
+ * @param holder - Record on disk.
159
+ * @param candidate - Record the caller would publish.
160
+ * @returns Whether re-entry is allowed.
161
+ */
162
+ export declare function isSameLockCaller(holder: WorktreeLockRecord, candidate: WorktreeLockRecord): boolean;
163
+ /**
164
+ * Read the current lock record for a task, if any.
165
+ *
166
+ * @param projectHash - Project hash.
167
+ * @param taskId - Task id.
168
+ * @returns The record, or `null` when no valid lock exists.
169
+ */
170
+ export declare function readWorktreeTaskLock(projectHash: string, taskId: string): WorktreeLockRecord | null;
171
+ /**
172
+ * Type guard for the error thrown by {@link acquireWorktreeTaskLock}.
173
+ *
174
+ * @param err - Any thrown value.
175
+ * @returns True when `err` is an `E_WORKTREE_LOCKED` error.
176
+ */
177
+ export declare function isWorktreeLockedError(err: unknown): err is WorktreeLockedError;
178
+ /**
179
+ * Atomically acquire the per-task worktree lock: create it, re-enter it (same
180
+ * caller), or reclaim it from a provably dead / unverifiable-and-stale holder.
181
+ *
182
+ * @param options - Task, holder identity, TTL and test seams.
183
+ * @returns How the lock was obtained plus the record now on disk.
184
+ * @throws {@link WorktreeLockedError} (`E_WORKTREE_LOCKED`) when a live holder
185
+ * owns the lock OR the lock could not be taken at all. Every failure of this
186
+ * function is a refusal: callers must never answer it with cleanup.
187
+ */
188
+ export declare function acquireWorktreeTaskLock(options: AcquireWorktreeTaskLockOptions): WorktreeLockAcquisition;
189
+ /**
190
+ * Refresh the holder's heartbeat. Only the holder that owns `token` may beat,
191
+ * and the rewrite happens under the lock mutex after re-checking the token, so
192
+ * it can never clobber a lock another contender reclaimed in the meantime.
193
+ *
194
+ * @param projectHash - Project hash.
195
+ * @param taskId - Task id.
196
+ * @param token - Token of the acquisition to refresh.
197
+ * @param now - Clock (default `Date.now`).
198
+ * @returns The refreshed record, or `null` when `token` no longer holds the lock.
199
+ */
200
+ export declare function heartbeatWorktreeTaskLock(projectHash: string, taskId: string, token: string, now?: () => number): WorktreeLockRecord | null;
201
+ /**
202
+ * Release the per-task lock. With `token`, only that acquisition's lock is
203
+ * removed (a lock since reclaimed or re-entered by someone else is left alone).
204
+ *
205
+ * @param projectHash - Project hash.
206
+ * @param taskId - Task id.
207
+ * @param token - Optional token of the acquisition to release.
208
+ * @returns True when a lock file was removed.
209
+ */
210
+ export declare function releaseWorktreeTaskLock(projectHash: string, taskId: string, token?: string): boolean;
211
+ //# sourceMappingURL=worktree-lock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worktree-lock.d.ts","sourceRoot":"","sources":["../src/worktree-lock.ts"],"names":[],"mappings":"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"}