tickmarkr 2.6.5 → 2.6.7

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.
Files changed (56) hide show
  1. package/README.md +7 -3
  2. package/dist/adapters/claude-code.js +8 -2
  3. package/dist/adapters/codex-commit-check.d.ts +16 -4
  4. package/dist/adapters/codex-commit-check.js +150 -26
  5. package/dist/adapters/codex.d.ts +1 -0
  6. package/dist/adapters/codex.js +20 -8
  7. package/dist/adapters/types.d.ts +4 -1
  8. package/dist/cli/commands/beat.d.ts +6 -1
  9. package/dist/cli/commands/beat.js +123 -76
  10. package/dist/cli/commands/doctor.js +3 -1
  11. package/dist/cli/commands/fleet.js +368 -85
  12. package/dist/cli/commands/init.js +23 -9
  13. package/dist/cli/commands/plan.js +12 -1
  14. package/dist/cli/commands/verify.js +37 -9
  15. package/dist/cli/help.d.ts +1 -0
  16. package/dist/cli/help.js +10 -5
  17. package/dist/cli/index.d.ts +1 -1
  18. package/dist/cli/index.js +1 -1
  19. package/dist/config/fleet-overlay.d.ts +14 -0
  20. package/dist/config/fleet-overlay.js +154 -25
  21. package/dist/drivers/orca.d.ts +17 -3
  22. package/dist/drivers/orca.js +64 -20
  23. package/dist/gates/baseline.d.ts +9 -0
  24. package/dist/gates/baseline.js +14 -3
  25. package/dist/gates/cache.js +1 -1
  26. package/dist/gates/review.js +15 -0
  27. package/dist/gates/run-gates.d.ts +20 -7
  28. package/dist/gates/run-gates.js +345 -210
  29. package/dist/gates/test-manifest.d.ts +11 -0
  30. package/dist/gates/test-manifest.js +151 -111
  31. package/dist/gates/timeout-shaped.d.ts +3 -0
  32. package/dist/gates/timeout-shaped.js +10 -0
  33. package/dist/run/beat-lifecycle.d.ts +154 -0
  34. package/dist/run/beat-lifecycle.js +825 -0
  35. package/dist/run/daemon.js +202 -126
  36. package/dist/run/git-trust.d.ts +27 -0
  37. package/dist/run/git-trust.js +178 -0
  38. package/dist/run/git.js +34 -14
  39. package/dist/run/journal.js +15 -3
  40. package/dist/run/lease.d.ts +23 -0
  41. package/dist/run/lease.js +232 -38
  42. package/dist/run/merge.js +2 -2
  43. package/dist/run/stall.js +20 -1
  44. package/dist/run/supervision.d.ts +8 -0
  45. package/dist/run/supervision.js +24 -4
  46. package/dist/run/verification-job.d.ts +51 -0
  47. package/dist/run/verification-job.js +137 -0
  48. package/dist/tui/ink/fleet-app.d.ts +35 -5
  49. package/dist/tui/ink/fleet-app.js +244 -124
  50. package/package.json +1 -1
  51. package/skills/tickmarkr-auto/SKILL.md +19 -10
  52. package/skills/tickmarkr-loop/SKILL.md +19 -10
  53. package/skills/tickmarkr-overseer/SKILL.md +25 -16
  54. package/skills/tickmarkr-overseer/scripts/classify-vitest-log.sh +58 -16
  55. package/skills/tickmarkr-overseer/scripts/grade-ci.sh +19 -6
  56. package/skills/tickmarkr-overseer/scripts/watch-parks.sh +121 -64
@@ -0,0 +1,178 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { lstatSync, readFileSync, realpathSync, statSync } from "node:fs";
3
+ import { basename, dirname, join } from "node:path";
4
+ // v2.6.7 T4 (GHSA-2): a seat-run linked worktree's gitdir is seat-writable (the Codex worker grant), so its
5
+ // `commondir` and `config.worktree` can redirect every later unsandboxed host git in that checkout to a
6
+ // hostile common directory or config program. Authority is derived afresh on every call from the seat-denied
7
+ // worktree gitfile and the trusted ordinary common directory's own bytes — never persisted enrollment, never
8
+ // the mutable commondir/config.worktree, never ambient GIT_DIR/GIT_COMMON_DIR. No verdict is cached: a check
9
+ // passed once proves nothing about the next spawn. This module must not import git.ts, lease.ts or journal.ts.
10
+ /** A covered git invocation refused before spawn; `path` names the offending metadata. */
11
+ export class GitTrustRefusal extends Error {
12
+ path;
13
+ constructor(path, detail) {
14
+ super(`git trust refusal: ${path}: ${detail}`);
15
+ this.name = "GitTrustRefusal";
16
+ this.path = path;
17
+ }
18
+ }
19
+ /** The nearest `.git` entry at or above cwd, or undefined outside any checkout. */
20
+ const dotGitOf = (cwd) => {
21
+ let dir;
22
+ try {
23
+ dir = realpathSync.native(cwd);
24
+ }
25
+ catch {
26
+ return undefined;
27
+ }
28
+ for (;;) {
29
+ const candidate = join(dir, ".git");
30
+ try {
31
+ lstatSync(candidate);
32
+ return candidate;
33
+ }
34
+ catch { /* keep walking */ }
35
+ const up = dirname(dir);
36
+ if (up === dir)
37
+ return undefined;
38
+ dir = up;
39
+ }
40
+ };
41
+ const present = (path) => {
42
+ try {
43
+ lstatSync(path);
44
+ return true;
45
+ }
46
+ catch {
47
+ return false;
48
+ }
49
+ };
50
+ /**
51
+ * A path exactly as git reads it from a gitfile or commondir (setup.c read_gitfile_gently /
52
+ * get_common_dir_noenv): raw bytes with ONLY trailing CR/LF stripped — never trimmed, so a leading or trailing
53
+ * space stays a path component git follows — relative to `base` unless it starts with "/", then a PHYSICAL
54
+ * realpath (symlinks resolved before `..`, as git's strbuf_realpath and realpath(3) do; fs.realpathSync
55
+ * normalizes `..` lexically first). Throws when the path does not resolve.
56
+ */
57
+ const resolveAsGit = (base, bytes) => {
58
+ let end = bytes.length;
59
+ while (end && (bytes[end - 1] === 0x0a || bytes[end - 1] === 0x0d))
60
+ end--;
61
+ const named = bytes.subarray(0, end);
62
+ return realpathSync.native(named[0] === 0x2f ? named : Buffer.concat([Buffer.from(`${base}/`), named]), { encoding: "buffer" });
63
+ };
64
+ function authorityOf(dotGit) {
65
+ let directory;
66
+ try {
67
+ directory = statSync(dotGit).isDirectory();
68
+ }
69
+ catch (e) {
70
+ throw new GitTrustRefusal(dotGit, `unreadable checkout metadata (${String(e)})`);
71
+ }
72
+ if (directory) {
73
+ const gitdir = realpathSync.native(dotGit);
74
+ return { gitdir, common: gitdir, linked: false };
75
+ }
76
+ // git accepts a gitfile only when its first bytes are exactly "gitdir: " and the rest names the path
77
+ let named;
78
+ try {
79
+ const bytes = readFileSync(dotGit);
80
+ if (bytes.subarray(0, 8).toString("latin1") === "gitdir: ")
81
+ named = bytes.subarray(8);
82
+ }
83
+ catch { /* refused below */ }
84
+ if (!named || !/[^\r\n]/.test(named.toString("latin1")))
85
+ throw new GitTrustRefusal(dotGit, "not a readable gitfile");
86
+ let gitdir;
87
+ try {
88
+ gitdir = resolveAsGit(dirname(dotGit), named).toString();
89
+ }
90
+ catch {
91
+ throw new GitTrustRefusal(dotGit, `names a missing gitdir ${named.toString().trimEnd()}`);
92
+ }
93
+ // Supported linked layout: <common>/worktrees/<name>, where <common> is itself an ordinary git directory.
94
+ // The layout decides, never commondir's presence: a seat can delete its own commondir (and plant standalone
95
+ // repository metadata beside it), which must not turn its writable gitdir into its own trusted common dir.
96
+ const common = basename(dirname(gitdir)) === "worktrees" ? dirname(dirname(gitdir)) : undefined;
97
+ if (common !== undefined && present(join(common, "HEAD")) && !present(join(common, "commondir")))
98
+ return { gitdir, common, linked: true };
99
+ // Outside that layout, a gitdir without commondir is an ordinary separate git directory (submodule, --separate-git-dir).
100
+ if (!present(join(gitdir, "commondir")))
101
+ return { gitdir, common: gitdir, linked: false };
102
+ if (common === undefined)
103
+ throw new GitTrustRefusal(dotGit, `names ${gitdir}, outside a supported <common>/worktrees/<name> layout`);
104
+ throw new GitTrustRefusal(common, "the linked worktree's common directory is not an ordinary git directory");
105
+ }
106
+ /**
107
+ * The canonical, independently expected common git directory for the checkout enclosing `cwd` — never what
108
+ * a mutable commondir says. Throws GitTrustRefusal naming the authority path when it cannot be established.
109
+ */
110
+ export function trustedCommonDir(cwd) {
111
+ const dotGit = dotGitOf(cwd);
112
+ if (!dotGit)
113
+ throw new GitTrustRefusal(cwd, "no checkout encloses this directory");
114
+ return authorityOf(dotGit).common;
115
+ }
116
+ /** Read failures and unparseable values never become clean authority. */
117
+ function worktreeConfigEnabled(common) {
118
+ const config = join(common, "config");
119
+ if (!present(config))
120
+ throw new GitTrustRefusal(config, "the trusted common config is missing");
121
+ try {
122
+ return execFileSync("git", ["config", "--file", config, "--type=bool", "--get", "extensions.worktreeConfig"], {
123
+ cwd: "/", encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], timeout: 15_000,
124
+ }).trim() === "true";
125
+ }
126
+ catch (error) {
127
+ const e = error;
128
+ if (e.status === 1 && !e.stderr?.trim())
129
+ return false; // the key is absent
130
+ throw new GitTrustRefusal(config, `cannot read extensions.worktreeConfig (${(e.stderr ?? String(error)).trim().split("\n")[0]})`);
131
+ }
132
+ }
133
+ /**
134
+ * The uncached pre-spawn check for a covered git invocation at `cwd`. Outside any checkout it is a no-op
135
+ * (unrelated shell commands stay compatible). Inside one it refuses, naming the file, when the commondir git
136
+ * would follow resolves anywhere but the expected common directory, or when the trusted common config enables
137
+ * extensions.worktreeConfig and the linked gitdir holds a config.worktree.
138
+ */
139
+ export function assertGitTrust(cwd) {
140
+ const dotGit = dotGitOf(cwd);
141
+ if (!dotGit)
142
+ return;
143
+ const { gitdir, common, linked } = authorityOf(dotGit);
144
+ const commondir = join(gitdir, "commondir");
145
+ if (linked && !present(commondir))
146
+ throw new GitTrustRefusal(commondir, `is missing, so git would treat the seat-writable gitdir as its own repository; expected ${common}`);
147
+ if (present(commondir)) {
148
+ // read as git reads it (resolveAsGit), never trimmed or lexically normalized: either would let a seat-written
149
+ // path that names the real common directory to us lead git somewhere else
150
+ let actual;
151
+ try {
152
+ actual = resolveAsGit(gitdir, readFileSync(commondir));
153
+ }
154
+ catch {
155
+ throw new GitTrustRefusal(commondir, `names no readable directory; expected ${common}`);
156
+ }
157
+ if (!actual.equals(Buffer.from(common)))
158
+ throw new GitTrustRefusal(commondir, `resolves to ${actual.toString()}, not the expected common directory ${common}`);
159
+ }
160
+ const worktreeConfig = join(gitdir, "config.worktree");
161
+ if (linked && present(worktreeConfig) && worktreeConfigEnabled(common)) {
162
+ throw new GitTrustRefusal(worktreeConfig, `extensions.worktreeConfig is enabled in ${join(common, "config")}, so git would read this seat-writable file`);
163
+ }
164
+ }
165
+ /** The inert hooks directory every protected git child receives (no hook can exist beneath it). */
166
+ export const INERT_HOOKS_PATH = "/dev/null";
167
+ const FORCED_GIT_CONFIG = `'core.fsmonitor=false' 'core.hooksPath=${INERT_HOOKS_PATH}'`;
168
+ /**
169
+ * A copy of `env` whose git children run with core.fsmonitor=false and an inert core.hooksPath, despite repo
170
+ * config and inherited GIT_CONFIG_COUNT/GIT_CONFIG_PARAMETERS: git applies GIT_CONFIG_PARAMETERS after
171
+ * GIT_CONFIG_COUNT and the last value wins, so the forced pair is appended last. Every other value survives.
172
+ * Named keys only — this does not neutralize arbitrary executable drivers. The pair is inherited by every
173
+ * descendant of the protected child (a suite that needs real hooks must clear it for its own git).
174
+ */
175
+ export function protectedGitEnv(env) {
176
+ const prior = env.GIT_CONFIG_PARAMETERS?.trim();
177
+ return { ...env, GIT_CONFIG_PARAMETERS: prior ? `${prior} ${FORCED_GIT_CONFIG}` : FORCED_GIT_CONFIG };
178
+ }
package/dist/run/git.js CHANGED
@@ -1,5 +1,6 @@
1
+ import { observeVerificationReceipt, observeVerificationSpawn, verificationJobEnvironment } from "./verification-job.js";
1
2
  import { executionSignal } from "./execution-budget.js";
2
- import { commandLeaseEnvironment, withCommandLease } from "./lease.js";
3
+ import { commandLeaseEnvironment, repositoryLeaseEnvironment, registerRepositoryChild, withCommandLease } from "./lease.js";
3
4
  import { AsyncLocalStorage } from "node:async_hooks";
4
5
  import { execFile, execFileSync, spawn } from "node:child_process";
5
6
  import { existsSync, lstatSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, readlinkSync, realpathSync, rmSync, statSync, symlinkSync, writeFileSync } from "node:fs";
@@ -10,6 +11,7 @@ import { shq } from "../adapters/types.js";
10
11
  import { tickmarkrDir } from "../graph/graph.js";
11
12
  import { ROUTING_ENV_SEAMS } from "../route/router.js";
12
13
  import { titleEnvironment } from "./title-environment.js";
14
+ import { assertGitTrust, protectedGitEnv, trustedCommonDir } from "./git-trust.js";
13
15
  export { ROUTING_ENV_SEAMS };
14
16
  // OBS-110: gate/baseline/tip-verify children are vitest suites; without a fork cap, concurrent
15
17
  // full-suite gate runs fork a worker-per-core pool per worktree and saturate the operator box.
@@ -289,6 +291,7 @@ export function shell(cmd, cwd, timeoutMs, login = false, options = {}) {
289
291
  activeShells.delete(activePid);
290
292
  activePid = undefined;
291
293
  }
294
+ observeVerificationReceipt({ ...receipt, confirmedStart });
292
295
  // Observational callbacks must not change process cleanup, retries or cancellation.
293
296
  try {
294
297
  options.onReceipt?.({ ...receipt, confirmedStart, ...(attribution ? { attribution: { ...attribution } } : {}) });
@@ -315,7 +318,9 @@ function executeShell(cmd, cwd, timeoutMs, login, options, observation) {
315
318
  // TICKMARKR_QUALITY leaked it into baseline/gate/tip-verify children, turning a dogfood
316
319
  // repo's route() tests red inside the gates. Scrub a copy at this one choke point so
317
320
  // children are hermetic by construction; the daemon's own process.env stays unchanged.
318
- const env = commandLeaseEnvironment(options.env ?? process.env);
321
+ // v2.6.7 T4 (E1): git children run with core.fsmonitor=false and an inert hooksPath whatever the
322
+ // repository or inherited GIT_CONFIG_* say; every other parent, capability and explicit value survives.
323
+ const env = protectedGitEnv(verificationJobEnvironment(repositoryLeaseEnvironment(commandLeaseEnvironment(options.env ?? process.env))));
319
324
  for (const k of ROUTING_ENV_SEAMS)
320
325
  delete env[k];
321
326
  // A run freezes the operator override and cores at startup; admission can lower the round cap.
@@ -346,6 +351,15 @@ function executeShell(cmd, cwd, timeoutMs, login, options, observation) {
346
351
  // SIGKILLing bash alone orphans grandchildren (codex/pi) that hold the stdio pipes
347
352
  // open, so "close" never fires and the promise wedges forever (v1.33.1 init hang).
348
353
  let p;
354
+ // v2.6.7 T4: the trust check runs before EVERY spawn attempt (retries included) and caches nothing — a
355
+ // seat can rewrite its gitdir's commondir/config.worktree between any two calls. A refusal names the file.
356
+ try {
357
+ assertGitTrust(cwd);
358
+ }
359
+ catch (error) {
360
+ observation.emit({ outcome: "spawn-failed", exitCode: null, signal: null, error: String(error) });
361
+ throw error;
362
+ }
349
363
  try {
350
364
  p = (spawnChild ?? spawn)("bash", [login ? "-lc" : "-c", cmd], { cwd, env, stdio: ["ignore", "pipe", "pipe"], detached: true });
351
365
  }
@@ -428,6 +442,19 @@ function executeShell(cmd, cwd, timeoutMs, login, options, observation) {
428
442
  boundDrain();
429
443
  };
430
444
  options.signal?.addEventListener("abort", abort, { once: true });
445
+ try {
446
+ registerRepositoryChild(p.pid);
447
+ }
448
+ catch (error) {
449
+ try {
450
+ process.kill(-p.pid, "SIGKILL");
451
+ }
452
+ catch {
453
+ p.kill("SIGKILL");
454
+ }
455
+ spawnError = String(error);
456
+ }
457
+ observeVerificationSpawn(p.pid);
431
458
  options.onSpawn?.(p.pid);
432
459
  if (options.signal?.aborted)
433
460
  abort();
@@ -693,21 +720,14 @@ export async function createWorktree(repo, branch, baseRef) {
693
720
  // match the provisioned SYMLINK, so a worker staging with `git add -A` commits the link and burns an
694
721
  // attempt on the scope gate. Write `node_modules` (no slash: matches the link too) into the exclude
695
722
  // file git actually consults for this worktree. Per-worktree `info/` is ignored in linked worktrees
696
- // (gitrepository-layout redirects it), so resolve through the `.git` gitfile + `commondir` indirection
697
- // to the common git dir. Local git metadata only — the target repository's .gitignore is never edited.
723
+ // (gitrepository-layout redirects it), so write the common git dir's exclude. Local git metadata only — the target repository's .gitignore is never edited.
698
724
  // Idempotent (appends only when no entry exists) and best-effort, like the link itself.
725
+ // v2.6.7 T4 (W1): the common dir is the independently derived one (trustedCommonDir), never what a
726
+ // seat-writable commondir names — a redirected commondir never receives the write.
699
727
  function excludeNodeModules(dir) {
700
728
  try {
701
- let gitDir = join(dir, ".git");
702
- if (lstatSync(gitDir).isFile()) { // linked worktree: .git is a gitfile naming the real git dir
703
- const m = /^gitdir:\s*(.+?)\s*$/m.exec(readFileSync(gitDir, "utf8"));
704
- if (!m)
705
- return;
706
- gitDir = resolve(dir, m[1]);
707
- }
708
- const commondir = join(gitDir, "commondir");
709
- if (existsSync(commondir))
710
- gitDir = resolve(gitDir, readFileSync(commondir, "utf8").trim());
729
+ lstatSync(join(dir, ".git")); // only the checkout itself, never an enclosing repository
730
+ const gitDir = trustedCommonDir(dir);
711
731
  const exclude = join(gitDir, "info", "exclude");
712
732
  const current = existsSync(exclude) ? readFileSync(exclude, "utf8") : "";
713
733
  if (/^node_modules$/m.test(current))
@@ -11,6 +11,7 @@ import { channelRouteIdentity, modelProvider } from "../route/preference.js";
11
11
  import { buildProfile, classify } from "../route/profile.js";
12
12
  import { DecisionEventSchema, trackJournalRows, } from "./protocol.js";
13
13
  import { PRESERVE_COMMIT_SUBJECT, PRESERVE_PRODUCER_TRAILER } from "./git.js";
14
+ import { assertGitTrust, protectedGitEnv } from "./git-trust.js";
14
15
  import { normalizeGateOutcome } from "./outcome.js";
15
16
  import { redactSecrets } from "./redact.js";
16
17
  export function phaseForGate(gate) {
@@ -2398,9 +2399,17 @@ export class Journal {
2398
2399
  // "unknown" — never zero — for a waiver without a well-formed obligation or a proof that no longer holds.
2399
2400
  export const OWED_CHECK_VERSION = 1;
2400
2401
  export const OWED_DISCHARGE_EVENT = "owed-check-discharged";
2401
- const owedGit = (cwd, args, input) => execFileSync("git", args, {
2402
- cwd, encoding: "utf8", maxBuffer: 64 * 1024 * 1024, ...(input === undefined ? { stdio: ["ignore", "pipe", "pipe"] } : { input }),
2403
- });
2402
+ // v2.6.7 T4: every owed-identity git call runs the uncached trust check and the forced child config, and its
2403
+ // diff-producing verbs never run an external diff or textconv driver; a refusal throws, so the fold is unknown.
2404
+ const NO_DRIVERS = new Set(["diff", "show", "log"]);
2405
+ const owedGit = (cwd, args, input) => {
2406
+ assertGitTrust(cwd);
2407
+ const argv = NO_DRIVERS.has(args[0]) ? [args[0], "--no-ext-diff", "--no-textconv", ...args.slice(1)] : args;
2408
+ return execFileSync("git", argv, {
2409
+ cwd, env: protectedGitEnv(process.env), encoding: "utf8", maxBuffer: 64 * 1024 * 1024,
2410
+ ...(input === undefined ? { stdio: ["ignore", "pipe", "pipe"] } : { input }),
2411
+ });
2412
+ };
2404
2413
  const sha256 = (text) => createHash("sha256").update(text).digest("hex");
2405
2414
  export const owedCriteria = (acceptance) => sha256(JSON.stringify(acceptance));
2406
2415
  /** The daemon's canonical gate subject for base..head (daemon.ts gateCommitSubject), byte for byte. */
@@ -2700,6 +2709,9 @@ export function foldOwedChecks(events, cwd, memo) {
2700
2709
  }
2701
2710
  }
2702
2711
  function foldOwedChecksOrThrow(events, cwd, memo) {
2712
+ // v2.6.7 T4 (G2): checkout trust is re-derived at the fold boundary, before a memoised proof can answer —
2713
+ // a memo hit runs no git, so hostile metadata would otherwise ride a clean fold's cached proof to known debt.
2714
+ assertGitTrust(cwd);
2703
2715
  const accepted = new Map();
2704
2716
  const discharged = new Set();
2705
2717
  const unknown = [];
@@ -27,6 +27,13 @@ export interface RepositoryLeaseHolder {
27
27
  cwd: string;
28
28
  at: number;
29
29
  token: string;
30
+ /** Exact owner birth and token-bound execution roots; retained across owner death. */
31
+ identity?: string;
32
+ protectOrphans?: true;
33
+ /** The owner settled; only its protected tree can still block reclamation. */
34
+ ownerReleased?: true;
35
+ roots?: number[];
36
+ rootBirths?: Record<string, string>;
30
37
  }
31
38
  export interface RepositoryLeaseOptions {
32
39
  pollMs?: number;
@@ -35,6 +42,13 @@ export interface RepositoryLeaseOptions {
35
42
  onWait?: (holder: RepositoryLeaseHolder) => void;
36
43
  /** The token a descendant inherited; defaults to REPOSITORY_LEASE_TOKEN_ENV. */
37
44
  inherited?: string;
45
+ /** Manifest jobs bridge capabilities through explicit child environments, never process.env. */
46
+ isolated?: boolean;
47
+ protectOrphans?: boolean;
48
+ onAdmission?: (holder: RepositoryLeaseHolder) => void;
49
+ onRelease?: () => void;
50
+ /** A new job inside an existing job cannot borrow that job's async capability. */
51
+ independent?: boolean;
38
52
  }
39
53
  /** OBS-880/1071: the file lease's token, exported to the holder's descendants for the span it is held
40
54
  * so a nested runner (a gate's suite under `tickmarkr verify`, a mutation child inside a leased suite)
@@ -68,4 +82,13 @@ export declare const readHolder: (path: string) => RepositoryLeaseHolder | undef
68
82
  export declare function tryReserve(path: string, mine: RepositoryLeaseHolder): boolean;
69
83
  /** Exposed for recovery regressions; the standalone verify uses the combined critical section. */
70
84
  export declare const reclaimDead: (path: string, identity: string, pollMs?: number) => Promise<boolean>;
85
+ export declare function protectedProcesses(holder: RepositoryLeaseHolder, tokenEnv?: string, ownedOnly?: boolean): Promise<number[]>;
86
+ export declare const repositoryLeaseEnvironment: (env: NodeJS.ProcessEnv) => NodeJS.ProcessEnv;
87
+ /** Called synchronously at spawn, before any user callback, and only on an owned generation. */
88
+ export declare function registerRepositoryChild(pid: number | undefined): void;
89
+ export declare const withFreshCommandLease: <T>(command: string, run: () => Promise<T>) => Promise<T>;
90
+ /** The standalone wrapper uses the same command-then-repository order when a scheduler exists.
91
+ * Without one (CLI/global setup), this remains exactly the outer file reservation API. */
71
92
  export declare function withRepositoryLease<T>(cwd: string, run: () => Promise<T>, opts?: RepositoryLeaseOptions): Promise<T>;
93
+ export declare const hasRepositoryLeaseOwnership: () => boolean;
94
+ export declare function reapRepositoryChildren(holder: RepositoryLeaseHolder, pollMs: number, tokenEnv?: string): Promise<void>;