tickmarkr 2.6.6 → 2.6.8

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 (51) hide show
  1. package/dist/adapters/claude-code.js +8 -2
  2. package/dist/adapters/codex-commit-check.d.ts +3 -2
  3. package/dist/adapters/codex-commit-check.js +37 -10
  4. package/dist/adapters/codex.js +10 -4
  5. package/dist/cli/commands/beat.d.ts +7 -2
  6. package/dist/cli/commands/beat.js +144 -77
  7. package/dist/cli/commands/fleet.js +66 -21
  8. package/dist/cli/commands/init.js +18 -7
  9. package/dist/cli/commands/plan.js +16 -18
  10. package/dist/cli/commands/verify.js +4 -2
  11. package/dist/cli/help.d.ts +1 -0
  12. package/dist/cli/help.js +14 -6
  13. package/dist/cli/index.d.ts +1 -1
  14. package/dist/cli/index.js +1 -1
  15. package/dist/drivers/herdr.js +2 -2
  16. package/dist/drivers/orca.d.ts +12 -4
  17. package/dist/drivers/orca.js +200 -39
  18. package/dist/gates/baseline.d.ts +9 -0
  19. package/dist/gates/baseline.js +14 -3
  20. package/dist/gates/cache.js +1 -1
  21. package/dist/gates/evidence.js +3 -3
  22. package/dist/gates/review.d.ts +1 -1
  23. package/dist/gates/review.js +77 -13
  24. package/dist/gates/run-gates.d.ts +21 -6
  25. package/dist/gates/run-gates.js +439 -224
  26. package/dist/gates/test-manifest.d.ts +42 -0
  27. package/dist/gates/test-manifest.js +203 -118
  28. package/dist/run/beat-lifecycle.d.ts +239 -0
  29. package/dist/run/beat-lifecycle.js +1782 -0
  30. package/dist/run/daemon.d.ts +4 -0
  31. package/dist/run/daemon.js +129 -40
  32. package/dist/run/git-trust.d.ts +102 -0
  33. package/dist/run/git-trust.js +334 -0
  34. package/dist/run/git.d.ts +24 -3
  35. package/dist/run/git.js +169 -30
  36. package/dist/run/journal.js +15 -3
  37. package/dist/run/lease.d.ts +23 -0
  38. package/dist/run/lease.js +232 -38
  39. package/dist/run/merge.js +4 -4
  40. package/dist/run/stall.js +20 -1
  41. package/dist/run/supervision.d.ts +8 -0
  42. package/dist/run/supervision.js +24 -4
  43. package/dist/run/verification-job.d.ts +53 -0
  44. package/dist/run/verification-job.js +159 -0
  45. package/dist/tui/ink/fleet-app.d.ts +16 -2
  46. package/dist/tui/ink/fleet-app.js +265 -141
  47. package/package.json +1 -1
  48. package/skills/tickmarkr-auto/SKILL.md +80 -27
  49. package/skills/tickmarkr-loop/SKILL.md +87 -27
  50. package/skills/tickmarkr-overseer/SKILL.md +125 -47
  51. package/skills/tickmarkr-overseer/scripts/watch-journal.sh +9 -7
@@ -0,0 +1,334 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { lstatSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, statSync, symlinkSync, writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { basename, dirname, join } from "node:path";
5
+ // v2.6.7 T4 (GHSA-2): a seat-run linked worktree's gitdir is seat-writable (the Codex worker grant), so its
6
+ // `commondir` and `config.worktree` can redirect every later unsandboxed host git in that checkout to a
7
+ // hostile common directory or config program. Authority is derived afresh on every call from the seat-denied
8
+ // worktree gitfile and the trusted ordinary common directory's own bytes — never persisted enrollment, never
9
+ // the mutable commondir/config.worktree, never ambient GIT_DIR/GIT_COMMON_DIR. No verdict is cached: a check
10
+ // passed once proves nothing about the next spawn. This module must not import git.ts, lease.ts or journal.ts.
11
+ /** A covered git invocation refused before spawn; `path` names the offending metadata. */
12
+ export class GitTrustRefusal extends Error {
13
+ path;
14
+ detail;
15
+ constructor(path, detail, options) {
16
+ super(`git trust refusal: ${path}: ${detail}`, options);
17
+ this.name = "GitTrustRefusal";
18
+ this.path = path;
19
+ this.detail = detail;
20
+ }
21
+ }
22
+ /** The nearest `.git` entry at or above cwd, or undefined outside any checkout. */
23
+ const dotGitOf = (cwd) => {
24
+ let dir;
25
+ try {
26
+ dir = realpathSync.native(cwd);
27
+ }
28
+ catch {
29
+ return undefined;
30
+ }
31
+ for (;;) {
32
+ const candidate = join(dir, ".git");
33
+ try {
34
+ lstatSync(candidate);
35
+ return candidate;
36
+ }
37
+ catch { /* keep walking */ }
38
+ const up = dirname(dir);
39
+ if (up === dir)
40
+ return undefined;
41
+ dir = up;
42
+ }
43
+ };
44
+ const present = (path) => {
45
+ try {
46
+ lstatSync(path);
47
+ return true;
48
+ }
49
+ catch {
50
+ return false;
51
+ }
52
+ };
53
+ /**
54
+ * A path exactly as git reads it from a gitfile or commondir (setup.c read_gitfile_gently /
55
+ * get_common_dir_noenv): raw bytes with ONLY trailing CR/LF stripped — never trimmed, so a leading or trailing
56
+ * space stays a path component git follows — relative to `base` unless it starts with "/", then a PHYSICAL
57
+ * realpath (symlinks resolved before `..`, as git's strbuf_realpath and realpath(3) do; fs.realpathSync
58
+ * normalizes `..` lexically first). Throws when the path does not resolve.
59
+ */
60
+ const resolveAsGit = (base, bytes) => {
61
+ let end = bytes.length;
62
+ while (end && (bytes[end - 1] === 0x0a || bytes[end - 1] === 0x0d))
63
+ end--;
64
+ const named = bytes.subarray(0, end);
65
+ return realpathSync.native(named[0] === 0x2f ? named : Buffer.concat([Buffer.from(`${base}/`), named]), { encoding: "buffer" });
66
+ };
67
+ function authorityOf(dotGit) {
68
+ let directory;
69
+ try {
70
+ directory = statSync(dotGit).isDirectory();
71
+ }
72
+ catch (e) {
73
+ throw new GitTrustRefusal(dotGit, `unreadable checkout metadata (${String(e)})`);
74
+ }
75
+ if (directory) {
76
+ const gitdir = realpathSync.native(dotGit);
77
+ return { gitdir, common: gitdir, linked: false };
78
+ }
79
+ // git accepts a gitfile only when its first bytes are exactly "gitdir: " and the rest names the path
80
+ let named;
81
+ try {
82
+ const bytes = readFileSync(dotGit);
83
+ if (bytes.subarray(0, 8).toString("latin1") === "gitdir: ")
84
+ named = bytes.subarray(8);
85
+ }
86
+ catch { /* refused below */ }
87
+ if (!named || !/[^\r\n]/.test(named.toString("latin1")))
88
+ throw new GitTrustRefusal(dotGit, "not a readable gitfile");
89
+ let gitdir;
90
+ try {
91
+ gitdir = resolveAsGit(dirname(dotGit), named).toString();
92
+ }
93
+ catch {
94
+ throw new GitTrustRefusal(dotGit, `names a missing gitdir ${named.toString().trimEnd()}`);
95
+ }
96
+ // Supported linked layout: <common>/worktrees/<name>, where <common> is itself an ordinary git directory.
97
+ // The layout decides, never commondir's presence: a seat can delete its own commondir (and plant standalone
98
+ // repository metadata beside it), which must not turn its writable gitdir into its own trusted common dir.
99
+ const common = basename(dirname(gitdir)) === "worktrees" ? dirname(dirname(gitdir)) : undefined;
100
+ if (common !== undefined && present(join(common, "HEAD")) && !present(join(common, "commondir")))
101
+ return { gitdir, common, linked: true };
102
+ // Outside that layout, a gitdir without commondir is an ordinary separate git directory (submodule, --separate-git-dir).
103
+ if (!present(join(gitdir, "commondir")))
104
+ return { gitdir, common: gitdir, linked: false };
105
+ if (common === undefined)
106
+ throw new GitTrustRefusal(dotGit, `names ${gitdir}, outside a supported <common>/worktrees/<name> layout`);
107
+ throw new GitTrustRefusal(common, "the linked worktree's common directory is not an ordinary git directory");
108
+ }
109
+ /**
110
+ * The canonical, independently expected common git directory for the checkout enclosing `cwd` — never what
111
+ * a mutable commondir says. Throws GitTrustRefusal naming the authority path when it cannot be established.
112
+ */
113
+ export function trustedCommonDir(cwd) {
114
+ const dotGit = dotGitOf(cwd);
115
+ if (!dotGit)
116
+ throw new GitTrustRefusal(cwd, "no checkout encloses this directory");
117
+ return authorityOf(dotGit).common;
118
+ }
119
+ /** One key of the trusted common config. Read failures and unparseable values never become clean authority. */
120
+ function trustedConfig(common, key, type) {
121
+ const config = join(common, "config");
122
+ if (!present(config))
123
+ throw new GitTrustRefusal(config, "the trusted common config is missing");
124
+ try {
125
+ return execFileSync("git", ["config", "--file", config, ...(type ? [`--type=${type}`] : []), "--get", key], {
126
+ cwd: "/", encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], timeout: 15_000,
127
+ }).trim();
128
+ }
129
+ catch (error) {
130
+ const e = error;
131
+ if (e.status === 1 && !e.stderr?.trim())
132
+ return undefined; // the key is absent
133
+ throw new GitTrustRefusal(config, `cannot read ${key} (${(e.stderr ?? String(error)).trim().split("\n")[0]})`);
134
+ }
135
+ }
136
+ const worktreeConfigEnabled = (common) => trustedConfig(common, "extensions.worktreeConfig", "bool") === "true";
137
+ /** commondir's identity: ctime moves on every write, rename, unlink/recreate or restore, and no caller can set it. */
138
+ const stampOf = (path) => {
139
+ try {
140
+ const s = lstatSync(path, { bigint: true });
141
+ return `${s.dev}:${s.ino}:${s.ctimeNs}`;
142
+ }
143
+ catch {
144
+ return undefined;
145
+ }
146
+ };
147
+ const isSymlink = (path) => { try {
148
+ return lstatSync(path).isSymbolicLink();
149
+ }
150
+ catch {
151
+ return false;
152
+ } };
153
+ /** The checked authority for `cwd` (see assertGitTrust), with the canonical worktree root the gitfile sits in. */
154
+ function checkedAuthority(cwd) {
155
+ const dotGit = dotGitOf(cwd);
156
+ if (!dotGit)
157
+ return undefined;
158
+ const { gitdir, common, linked } = authorityOf(dotGit);
159
+ const commondir = join(gitdir, "commondir");
160
+ // stamped BEFORE its bytes are read, so any change after this read moves the stamp
161
+ const stamp = linked ? stampOf(commondir) : undefined;
162
+ if (linked && !present(commondir))
163
+ throw new GitTrustRefusal(commondir, `is missing, so git would treat the seat-writable gitdir as its own repository; expected ${common}`);
164
+ // git writes commondir as a regular file; through a symlink the bytes git reads could be rewritten and restored
165
+ // without moving the link's own stamp, so that layout is refused rather than trusted
166
+ if (linked && isSymlink(commondir))
167
+ throw new GitTrustRefusal(commondir, "is a symbolic link, so its target could change without moving its stamp");
168
+ if (present(commondir)) {
169
+ // read as git reads it (resolveAsGit), never trimmed or lexically normalized: either would let a seat-written
170
+ // path that names the real common directory to us lead git somewhere else
171
+ let actual;
172
+ try {
173
+ actual = resolveAsGit(gitdir, readFileSync(commondir));
174
+ }
175
+ catch {
176
+ throw new GitTrustRefusal(commondir, `names no readable directory; expected ${common}`);
177
+ }
178
+ if (!actual.equals(Buffer.from(common)))
179
+ throw new GitTrustRefusal(commondir, `resolves to ${actual.toString()}, not the expected common directory ${common}`);
180
+ }
181
+ const worktreeConfig = join(gitdir, "config.worktree");
182
+ if (linked && present(worktreeConfig) && worktreeConfigEnabled(common)) {
183
+ throw new GitTrustRefusal(worktreeConfig, `extensions.worktreeConfig is enabled in ${join(common, "config")}, so git would read this seat-writable file`);
184
+ }
185
+ return { gitdir, common, linked, worktree: dirname(dotGit), stamp };
186
+ }
187
+ /**
188
+ * The uncached pre-spawn check for a covered git invocation at `cwd`. Outside any checkout it is a no-op
189
+ * (unrelated shell commands stay compatible). Inside one it refuses, naming the file, when the commondir git
190
+ * would follow resolves anywhere but the expected common directory, or when the trusted common config enables
191
+ * extensions.worktreeConfig and the linked gitdir holds a config.worktree.
192
+ */
193
+ export function assertGitTrust(cwd) {
194
+ checkedAuthority(cwd);
195
+ }
196
+ function storePin(store, env) {
197
+ if (!present(join(store, "HEAD")) || present(join(store, "commondir")))
198
+ throw new GitTrustRefusal(store, "is not an ordinary git directory");
199
+ const child = { ...env, GIT_DIR: store };
200
+ for (const k of ["GIT_COMMON_DIR", "GIT_WORK_TREE", "GIT_REFERENCE_BACKEND", "GIT_REF_STORAGE_FORMAT", "GIT_INDEX_FILE", "GIT_OBJECT_DIRECTORY", "GIT_ALTERNATE_OBJECT_DIRECTORIES"])
201
+ delete child[k];
202
+ return { env: child, store, recheck: () => { }, release: () => { }, unview: (text) => text };
203
+ }
204
+ /**
205
+ * v2.6.8 T2: the ref store an own-git child in a linked checkout is pinned to — a private directory of this attempt
206
+ * whose `refs`, `packed-refs`, `logs`, `worktrees` and `HEAD` are links into the TRUSTED common directory, beside a
207
+ * `HEAD.lock` nothing removes. git decides "linked" from the gitdir's commondir FILE alone: while it is present the
208
+ * child reads this checkout's own HEAD from <view>/worktrees/<id> (its trusted gitdir) and shared refs from the trusted
209
+ * store; when a seat REMOVES it before the child starts, git treats the view as the repository, so no HEAD-relative
210
+ * write (commit, merge, cherry-pick, reset, checkout, update-ref HEAD) can take the view's HEAD lock and nothing lands
211
+ * on the main checkout's branch — the recheck then refuses the result. That lock also refuses a write of the MAIN
212
+ * worktree's HEAD from a linked checkout's own git. Only the files format can be viewed this way: any other
213
+ * `extensions.refStorage` is refused by name, never run unpinned.
214
+ */
215
+ function refStoreView(common) {
216
+ const view = realpathSync(mkdtempSync(join(tmpdir(), "tickmarkr-refs-")));
217
+ const release = () => rmSync(view, { recursive: true, force: true }); // unlinks the links, never their targets
218
+ try {
219
+ for (const name of ["refs", "packed-refs", "logs", "worktrees", "HEAD"])
220
+ symlinkSync(join(common, name), join(view, name));
221
+ writeFileSync(join(view, "HEAD.lock"), "");
222
+ }
223
+ catch (error) {
224
+ release();
225
+ throw error;
226
+ }
227
+ return { uri: `files://${view}`, release, unview: (text) => text.split(view).join(common) };
228
+ }
229
+ function assertFilesRefStore(common) {
230
+ const format = trustedConfig(common, "extensions.refStorage") ?? "files";
231
+ if (format !== "files") {
232
+ throw new GitTrustRefusal(join(common, "config"), `extensions.refStorage is ${format}; tickmarkr's own git can pin only the files ref store, so it is refused instead of running unpinned`);
233
+ }
234
+ }
235
+ /** A detached auto gc or maintenance would outlive its child and read refs through a view already released. */
236
+ const NO_BACKGROUND_GIT = "'gc.auto=0' 'maintenance.auto=false'";
237
+ const PROBE_REF = "refs/tickmarkr/ref-pin-probe", PROBE_TARGET = "refs/tickmarkr/ref-pin-honoured";
238
+ /**
239
+ * Whether, before the child spawns, the git its PATH resolves honours GIT_REFERENCE_BACKEND (git >= 2.54): a fresh
240
+ * probe repository whose own refs lack PROBE_REF, pinned to a fresh store that holds it as a symbolic ref. A git that
241
+ * ignores the pin reads the probe repository's own refs. Nothing is cached: each attempt probes again.
242
+ */
243
+ function refStorePinHonoured(env) {
244
+ const dir = mkdtempSync(join(tmpdir(), "tickmarkr-ref-pin-"));
245
+ try {
246
+ const repo = join(dir, "repo"), store = join(dir, "store");
247
+ for (const d of [join(repo, "objects"), join(repo, "refs"), join(store, "refs", "tickmarkr")])
248
+ mkdirSync(d, { recursive: true });
249
+ for (const head of [join(repo, "HEAD"), join(store, "HEAD")])
250
+ writeFileSync(head, "ref: refs/heads/main\n");
251
+ writeFileSync(join(store, PROBE_REF), `ref: ${PROBE_TARGET}\n`);
252
+ const probe = { ...env, GIT_DIR: repo, GIT_REFERENCE_BACKEND: `files://${store}` };
253
+ for (const k of ["GIT_COMMON_DIR", "GIT_WORK_TREE", "GIT_INDEX_FILE", "GIT_OBJECT_DIRECTORY", "GIT_ALTERNATE_OBJECT_DIRECTORIES"])
254
+ delete probe[k];
255
+ let read = "";
256
+ try {
257
+ read = execFileSync("git", ["symbolic-ref", PROBE_REF], { cwd: dir, env: probe, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], timeout: 15_000 }).trim();
258
+ }
259
+ catch { /* refused below */ }
260
+ return read === PROBE_TARGET;
261
+ }
262
+ finally {
263
+ rmSync(dir, { recursive: true, force: true });
264
+ }
265
+ }
266
+ let warnedRefStorePin = false;
267
+ /** Said once per process: the fallback below is a weaker guard, and the operator should know why and what lifts it. */
268
+ function warnRefStorePinUnavailable() {
269
+ if (warnedRefStorePin)
270
+ return;
271
+ warnedRefStorePin = true;
272
+ console.error("tickmarkr: the git on this PATH does not honour GIT_REFERENCE_BACKEND (needs git 2.54 or newer), so tickmarkr's own git in linked checkouts runs without its ref-store pin — path pins and the post-exit commondir recheck only, the 2.6.7 guard; upgrade git for the full pin");
273
+ }
274
+ /**
275
+ * v2.6.8 T2 (P3): the same uncached check, returning the child of tickmarkr's OWN git in a linked checkout — `env`
276
+ * (the caller's) with GIT_DIR, GIT_COMMON_DIR and GIT_WORK_TREE set to the canonical gitdir, trusted common directory
277
+ * and worktree root derived above, AND the ref store: GIT_REFERENCE_BACKEND names this attempt's private view of the
278
+ * trusted store (refStoreView), since git's ref store otherwise resolves shared refs through the gitdir's commondir
279
+ * FILE even under GIT_COMMON_DIR. So a commondir rewritten after this check (by a live worker or a surviving
280
+ * descendant) is followed for neither config, hooks, info/, objects nor refs: HEAD, branches, status and diffs read
281
+ * the real repository and every ref write lands in the trusted store, whatever an inherited GIT_CONFIG_* or ref backend
282
+ * says; a commondir REMOVED after it cannot move the main checkout's branch. Before such a child spawns, the git it
283
+ * would run must prove it honours that pin (refStorePinHonoured); one that cannot (git < 2.54) runs without it, below.
284
+ * Nothing here reads the command, and no caller leaves any pin. `recheck` (after the child exits) fails closed on ANY change to commondir since the check: the result
285
+ * of that child is never returned. An ordinary checkout or a non-repository gets no pin.
286
+ * NAMED RESIDUAL: git still reads $GIT_DIR/config.worktree when the trusted common config already enables
287
+ * extensions.worktreeConfig, so a config.worktree planted AFTER this check is not closed.
288
+ */
289
+ export function ownGitPin(cwd, env, capability = {}) {
290
+ if ("store" in capability)
291
+ return storePin(capability.store, env);
292
+ const { createsWorktree } = capability;
293
+ const authority = checkedAuthority(cwd);
294
+ if (!authority?.linked)
295
+ return undefined;
296
+ const { gitdir, common, worktree, stamp } = authority;
297
+ const child = { ...env, GIT_DIR: gitdir, GIT_COMMON_DIR: common, GIT_WORK_TREE: worktree,
298
+ GIT_CONFIG_PARAMETERS: `${env.GIT_CONFIG_PARAMETERS?.trim() ?? ""} ${NO_BACKGROUND_GIT}`.trim() };
299
+ // GIT_REF_STORAGE_FORMAT only chooses a NEW repository's format; an inherited one is never a store this child follows
300
+ delete child.GIT_REF_STORAGE_FORMAT;
301
+ assertFilesRefStore(common);
302
+ const commondir = join(gitdir, "commondir");
303
+ const recheck = () => {
304
+ if (stampOf(commondir) !== stamp) {
305
+ throw new GitTrustRefusal(commondir, "changed while tickmarkr's own git ran in this checkout, so that result is refused");
306
+ }
307
+ };
308
+ // D-1484: a git older than 2.54 cannot take the ref-store pin. Refusing it would refuse tickmarkr's own git in every
309
+ // linked checkout on most installed gits, and a hostile git binary ignores every pin anyway; so it runs with the
310
+ // path pins and the post-exit recheck (the 2.6.7 guard). NAMED RESIDUAL there: refs still resolve through the
311
+ // commondir file, so a rewrite between check and exit can reach the hostile store before the recheck refuses.
312
+ if (!refStorePinHonoured(child)) {
313
+ delete child.GIT_REFERENCE_BACKEND;
314
+ warnRefStorePinUnavailable();
315
+ return { env: child, store: common, release: () => { }, unview: (text) => text, recheck };
316
+ }
317
+ const view = createsWorktree === "detached" ? { uri: `files://${common}`, release: () => { }, unview: (text) => text } : refStoreView(common);
318
+ child.GIT_REFERENCE_BACKEND = view.uri;
319
+ return { env: child, store: common, release: view.release, unview: view.unview, recheck };
320
+ }
321
+ /** The inert hooks directory every protected git child receives (no hook can exist beneath it). */
322
+ export const INERT_HOOKS_PATH = "/dev/null";
323
+ const FORCED_GIT_CONFIG = `'core.fsmonitor=false' 'core.hooksPath=${INERT_HOOKS_PATH}'`;
324
+ /**
325
+ * A copy of `env` whose git children run with core.fsmonitor=false and an inert core.hooksPath, despite repo
326
+ * config and inherited GIT_CONFIG_COUNT/GIT_CONFIG_PARAMETERS: git applies GIT_CONFIG_PARAMETERS after
327
+ * GIT_CONFIG_COUNT and the last value wins, so the forced pair is appended last. Every other value survives.
328
+ * Named keys only — this does not neutralize arbitrary executable drivers. v2.6.8 T2: only tickmarkr's OWN git
329
+ * children (shGit/shGitOk) receive it; payload shells (gate commands, suites) keep the operator's git config.
330
+ */
331
+ export function protectedGitEnv(env) {
332
+ const prior = env.GIT_CONFIG_PARAMETERS?.trim();
333
+ return { ...env, GIT_CONFIG_PARAMETERS: prior ? `${prior} ${FORCED_GIT_CONFIG}` : FORCED_GIT_CONFIG };
334
+ }
package/dist/run/git.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { CommandReceiptAttribution, ShellReceipt } from "./protocol.js";
2
2
  import { spawn } from "node:child_process";
3
3
  import { ROUTING_ENV_SEAMS } from "../route/router.js";
4
+ import { type OwnGitCapability } from "./git-trust.js";
4
5
  export { ROUTING_ENV_SEAMS };
5
6
  export declare const FORK_CAP_ENV = "VITEST_MAX_FORKS";
6
7
  export declare const SUITE_PARENT_ENV = "TICKMARKR_SUITE_PARENT";
@@ -168,12 +169,32 @@ export interface ShellOptions {
168
169
  /** Called once per invocation (1-based), including each pre-spawn retry. */
169
170
  receiptAttribution?: (invocation: number) => CommandReceiptAttribution;
170
171
  }
171
- /** Shared command seam, including invocation-bound manifested runners. */
172
+ /** Shared command seam, including invocation-bound manifested runners. A PAYLOAD entry: its children keep the
173
+ * operator's git config (hooks, fsmonitor) and get no checkout pin — ownership follows the entry, never `login`
174
+ * or the command text. tickmarkr's own git goes through shGit/shGitOk. */
172
175
  export declare function shell(cmd: string, cwd: string, timeoutMs: number, login?: boolean, options?: ShellOptions): Promise<ShResult>;
176
+ /**
177
+ * v2.6.8 T2: what an OWN-GIT caller declares at its call site — never inferred from the command text. Every own-git
178
+ * child in a linked checkout keeps the path pin and the ref-store pin; `createsWorktree` is the worktree capability the
179
+ * branch-creating call sites declare, and addDetachedWorktree is the detached one (see OwnGitCapability and ownGitPin).
180
+ */
181
+ export type OwnGitOptions = OwnGitCapability;
173
182
  export declare function sh(cmd: string, cwd: string, timeoutMs?: number, options?: ShellOptions): Promise<ShResult>;
174
- export declare function shGit(cmd: string, cwd: string, timeoutMs?: number): Promise<ShResult>;
183
+ export declare function shGit(cmd: string, cwd: string, timeoutMs?: number, own?: OwnGitOptions): Promise<ShResult>;
175
184
  export declare function shOk(cmd: string, cwd: string): Promise<string>;
176
- export declare function shGitOk(cmd: string, cwd: string): Promise<string>;
185
+ export declare function shGitOk(cmd: string, cwd: string, own?: OwnGitOptions): Promise<string>;
186
+ /**
187
+ * v2.6.8 T2: the DETACHED worktree capability, declared by calling it (the daemon's baseline recapture and standalone
188
+ * verify's base worktree). Under the ref-store pin git stubs a detached add's new HEAD as `ref: refs/heads/.invalid`
189
+ * and checks out nothing, and no own-git step ever runs unpinned instead: the commit-ish is resolved to an object id in
190
+ * `cwd`'s checkout (HEAD and branch names read the trusted store), the checkout is added `--no-checkout --detach` at
191
+ * that id (its ref store pinned to the trusted common directory, see DetachedAddCapability), and then — pinned to the
192
+ * NEW checkout's own authority — its HEAD is written as that id, git's `refs/heads/.invalid` stub ref is deleted and the
193
+ * checkout is populated.
194
+ * A step raced by a commondir change is refused by name before the next one runs, and what the add created is rolled
195
+ * back through the trusted common directory derived here once (dropWorktree), even while that commondir stays hostile.
196
+ */
197
+ export declare function addDetachedWorktree(cwd: string, dir: string, commitish: string, timeoutMs?: number): Promise<ShResult>;
177
198
  export declare function npmDependencyManifestChanged(cwd: string, baselineRef: string): Promise<boolean>;
178
199
  export declare function npmDependencyInstallCommand(cwd: string): string;
179
200
  export declare function gitHead(cwd: string): Promise<string>;