@astrosheep/keiyaku 4.6.4 → 4.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 (247) hide show
  1. package/README.md +7 -0
  2. package/build/{integrations/marketplace → plugins}/.agents/plugins/marketplace.json +1 -1
  3. package/build/plugins/.claude-plugin/marketplace.json +5 -0
  4. package/build/{integrations/marketplace/plugins/keiyaku → plugins/claude}/.claude-plugin/plugin.json +1 -1
  5. package/build/{integrations/marketplace/plugins/keiyaku → plugins/claude}/skills/keiyaku-akuma/SKILL.md +43 -8
  6. package/build/{integrations/marketplace/plugins/keiyaku → plugins/claude}/skills/keiyaku-bind/SKILL.md +7 -8
  7. package/build/{integrations/marketplace/plugins/keiyaku → plugins/claude}/skills/keiyaku-workflow/SKILL.md +3 -3
  8. package/build/{integrations/marketplace/plugins/keiyaku → plugins/codex}/.codex-plugin/plugin.json +1 -1
  9. package/build/plugins/codex/skills/keiyaku/SKILL.md +44 -0
  10. package/build/plugins/codex/skills/keiyaku-akuma/SKILL.md +130 -0
  11. package/build/plugins/codex/skills/keiyaku-akuma/references/automation.md +269 -0
  12. package/build/plugins/codex/skills/keiyaku-babysit/SKILL.md +65 -0
  13. package/build/plugins/codex/skills/keiyaku-bind/SKILL.md +216 -0
  14. package/build/plugins/codex/skills/keiyaku-bind/agents/openai.yaml +4 -0
  15. package/build/plugins/codex/skills/keiyaku-task/SKILL.md +104 -0
  16. package/build/plugins/codex/skills/keiyaku-workflow/SKILL.md +129 -0
  17. package/build/plugins/opencode/package.json +6 -0
  18. package/build/plugins/opencode/skills/keiyaku/SKILL.md +44 -0
  19. package/build/plugins/opencode/skills/keiyaku-akuma/SKILL.md +130 -0
  20. package/build/plugins/opencode/skills/keiyaku-akuma/references/automation.md +269 -0
  21. package/build/plugins/opencode/skills/keiyaku-babysit/SKILL.md +65 -0
  22. package/build/plugins/opencode/skills/keiyaku-bind/SKILL.md +216 -0
  23. package/build/plugins/opencode/skills/keiyaku-bind/agents/openai.yaml +4 -0
  24. package/build/plugins/opencode/skills/keiyaku-task/SKILL.md +104 -0
  25. package/build/plugins/opencode/skills/keiyaku-workflow/SKILL.md +129 -0
  26. package/build/skills/keiyaku/SKILL.md +44 -0
  27. package/build/skills/keiyaku-akuma/SKILL.md +130 -0
  28. package/build/skills/keiyaku-akuma/references/automation.md +269 -0
  29. package/build/skills/keiyaku-babysit/SKILL.md +65 -0
  30. package/build/skills/keiyaku-bind/SKILL.md +216 -0
  31. package/build/skills/keiyaku-bind/agents/openai.yaml +4 -0
  32. package/build/skills/keiyaku-task/SKILL.md +104 -0
  33. package/build/skills/keiyaku-workflow/SKILL.md +129 -0
  34. package/build/src/akuma/akuma-instance.js +7 -5
  35. package/build/src/akuma/akuma-observe.d.ts +22 -15
  36. package/build/src/akuma/akuma-observe.js +87 -16
  37. package/build/src/akuma/akuma-owner.d.ts +3 -2
  38. package/build/src/akuma/akuma-owner.js +28 -10
  39. package/build/src/akuma/akuma-selection-execution.d.ts +3 -1
  40. package/build/src/akuma/akuma-selection-execution.js +2 -1
  41. package/build/src/akuma/akuma-wait.d.ts +6 -4
  42. package/build/src/akuma/akuma-wait.js +78 -53
  43. package/build/src/akuma/akuma.d.ts +6 -4
  44. package/build/src/akuma/akuma.js +4 -0
  45. package/build/src/akuma/body.d.ts +1 -0
  46. package/build/src/akuma/body.js +12 -23
  47. package/build/src/akuma/call-initial-tell.d.ts +2 -2
  48. package/build/src/akuma/call-initial-tell.js +1 -1
  49. package/build/src/akuma/call-request.d.ts +2 -2
  50. package/build/src/akuma/heart/index.d.ts +7 -2
  51. package/build/src/akuma/heart/index.js +50 -5
  52. package/build/src/akuma/heart/tells.d.ts +33 -2
  53. package/build/src/akuma/heart/tells.js +24 -18
  54. package/build/src/akuma/heart/timeline.d.ts +23 -1
  55. package/build/src/akuma/heart/timeline.js +28 -21
  56. package/build/src/akuma/projection.d.ts +10 -10
  57. package/build/src/akuma/providers/pi/index.js +72 -39
  58. package/build/src/akuma/selection-observation.d.ts +20 -20
  59. package/build/src/akuma/selection-owner-port.d.ts +1 -1
  60. package/build/src/akuma/selection-owner-port.js +16 -5
  61. package/build/src/akuma/selection-request.d.ts +2 -0
  62. package/build/src/akuma/selection-request.js +2 -1
  63. package/build/src/akuma-body.js +5 -2
  64. package/build/src/alias/index.d.ts +0 -1
  65. package/build/src/alias/index.js +0 -4
  66. package/build/src/body/amend.d.ts +0 -4
  67. package/build/src/body/amend.js +0 -3
  68. package/build/src/body/region.d.ts +0 -1
  69. package/build/src/body/region.js +0 -3
  70. package/build/src/body/verification.d.ts +0 -1
  71. package/build/src/cli/commands/akuma-grammar.js +23 -66
  72. package/build/src/cli/commands/akuma.js +23 -42
  73. package/build/src/cli/commands/contract-grammar.d.ts +14 -1
  74. package/build/src/cli/commands/contract-grammar.js +54 -21
  75. package/build/src/cli/commands/contract-help.d.ts +9 -8
  76. package/build/src/cli/commands/contract-help.js +14 -7
  77. package/build/src/cli/commands/contract.js +57 -117
  78. package/build/src/cli/commands/install.d.ts +5 -5
  79. package/build/src/cli/commands/install.js +4 -7
  80. package/build/src/cli/commands/task-grammar.js +10 -66
  81. package/build/src/cli/commands/task.d.ts +0 -1
  82. package/build/src/cli/commands/task.js +34 -53
  83. package/build/src/cli/coordinates.d.ts +1 -2
  84. package/build/src/cli/coordinates.js +1 -4
  85. package/build/src/cli/parse.d.ts +2 -3
  86. package/build/src/cli/parse.js +24 -66
  87. package/build/src/cli/render/akuma.d.ts +3 -16
  88. package/build/src/cli/render/akuma.js +1 -13
  89. package/build/src/cli/render/audit.js +1 -1
  90. package/build/src/cli/render/contract.js +34 -32
  91. package/build/src/cli/render/kanshi-akuma.d.ts +8 -2
  92. package/build/src/cli/render/kanshi-akuma.js +14 -7
  93. package/build/src/cli/render/kanshi.d.ts +3 -1
  94. package/build/src/cli/render/kanshi.js +44 -32
  95. package/build/src/cli/render/receipt.js +6 -1
  96. package/build/src/cli/render/task.d.ts +1 -1
  97. package/build/src/cli/render/task.js +3 -27
  98. package/build/src/cli/render/terminal.d.ts +6 -2
  99. package/build/src/cli/render/terminal.js +8 -2
  100. package/build/src/cli/runtime.js +3 -16
  101. package/build/src/cli/scan.d.ts +26 -0
  102. package/build/src/cli/scan.js +82 -0
  103. package/build/src/cli/streams.d.ts +5 -0
  104. package/build/src/cli/streams.js +11 -0
  105. package/build/src/contract-worktree.js +38 -21
  106. package/build/src/core/facts/codec.d.ts +1 -2
  107. package/build/src/core/facts/codec.js +5 -11
  108. package/build/src/core/facts/types.d.ts +16 -2
  109. package/build/src/core/facts/types.js +15 -0
  110. package/build/src/core/verbs/abandon.js +3 -9
  111. package/build/src/core/verbs/amend.js +2 -9
  112. package/build/src/core/verbs/arc.js +6 -13
  113. package/build/src/core/verbs/attestation.js +2 -9
  114. package/build/src/core/verbs/bind.js +2 -9
  115. package/build/src/core/verbs/deliver.js +2 -9
  116. package/build/src/core/verbs/placement.d.ts +1 -1
  117. package/build/src/core/verbs/placement.js +2 -9
  118. package/build/src/core/verbs/reintegrate.d.ts +1 -1
  119. package/build/src/core/verbs/reintegrate.js +2 -9
  120. package/build/src/dispatch/index.d.ts +2 -2
  121. package/build/src/dispatch/index.js +33 -11
  122. package/build/src/git/hooks.d.ts +2 -4
  123. package/build/src/git/hooks.js +0 -12
  124. package/build/src/git/integration.d.ts +5 -3
  125. package/build/src/git/integration.js +36 -25
  126. package/build/src/git/nuke.js +4 -4
  127. package/build/src/git/observe.d.ts +4 -4
  128. package/build/src/git/observe.js +5 -5
  129. package/build/src/git/process.d.ts +1 -4
  130. package/build/src/git/process.js +1 -11
  131. package/build/src/git/read-observation.js +1 -1
  132. package/build/src/git/reconcile.d.ts +1 -1
  133. package/build/src/git/reconcile.js +15 -13
  134. package/build/src/git/repository.d.ts +2 -6
  135. package/build/src/git/repository.js +18 -31
  136. package/build/src/git/scratch.d.ts +6 -6
  137. package/build/src/git/scratch.js +110 -46
  138. package/build/src/git/target-placement.d.ts +5 -5
  139. package/build/src/git/target-placement.js +18 -33
  140. package/build/src/git/tender.d.ts +6 -2
  141. package/build/src/git/tender.js +19 -12
  142. package/build/src/git/terminal-reconcile.js +24 -13
  143. package/build/src/git/terminal-seal.d.ts +2 -5
  144. package/build/src/git/terminal-seal.js +5 -3
  145. package/build/src/git/workspace.d.ts +17 -13
  146. package/build/src/git/workspace.js +58 -41
  147. package/build/src/kanshi/index.d.ts +2 -2
  148. package/build/src/kanshi/index.js +1 -1
  149. package/build/src/kanshi/read.d.ts +8 -1
  150. package/build/src/kanshi/read.js +72 -7
  151. package/build/src/kanshi/report.d.ts +12 -0
  152. package/build/src/library/akuma-creation.d.ts +4 -4
  153. package/build/src/library/akuma-creation.js +21 -19
  154. package/build/src/library/bind.d.ts +1 -63
  155. package/build/src/library/bind.js +3 -9
  156. package/build/src/library/contract-creation.js +2 -30
  157. package/build/src/library/contract-execution.d.ts +0 -1
  158. package/build/src/library/contract-execution.js +9 -34
  159. package/build/src/library/contract-operations.d.ts +5 -1
  160. package/build/src/library/contract-operations.js +7 -3
  161. package/build/src/library/contract-types.d.ts +1 -1
  162. package/build/src/library/delivery.d.ts +91 -93
  163. package/build/src/library/delivery.js +2 -1
  164. package/build/src/library/input.d.ts +0 -2
  165. package/build/src/library/input.js +0 -4
  166. package/build/src/library/invocation.d.ts +0 -1
  167. package/build/src/library/invocation.js +3 -25
  168. package/build/src/library/outcome.d.ts +651 -602
  169. package/build/src/library/outcome.js +4 -4
  170. package/build/src/library/reconcile.d.ts +6 -0
  171. package/build/src/library/reconcile.js +31 -1
  172. package/build/src/library/repo.d.ts +0 -1
  173. package/build/src/library/selection.d.ts +1 -10
  174. package/build/src/library/selection.js +19 -17
  175. package/build/src/plugin/akuma-signals.d.ts +10 -11
  176. package/build/src/plugin/akuma-signals.js +36 -20
  177. package/build/src/plugin/public.d.ts +5 -0
  178. package/build/src/plugin/runtime.d.ts +1 -0
  179. package/build/src/plugin/runtime.js +34 -23
  180. package/build/src/protocol/abandon.d.ts +1 -1
  181. package/build/src/protocol/abandon.js +3 -10
  182. package/build/src/protocol/amend.js +2 -5
  183. package/build/src/protocol/arc.d.ts +1 -1
  184. package/build/src/protocol/arc.js +2 -9
  185. package/build/src/protocol/attempt.d.ts +3 -20
  186. package/build/src/protocol/attempt.js +6 -5
  187. package/build/src/protocol/audit.d.ts +1 -0
  188. package/build/src/protocol/audit.js +10 -11
  189. package/build/src/protocol/completion.d.ts +129 -55
  190. package/build/src/protocol/completion.js +62 -73
  191. package/build/src/protocol/deliver.d.ts +7 -5
  192. package/build/src/protocol/deliver.js +74 -44
  193. package/build/src/protocol/execution-observation.d.ts +2 -4
  194. package/build/src/protocol/execution-observation.js +3 -7
  195. package/build/src/protocol/intent.d.ts +1 -1
  196. package/build/src/protocol/intent.js +1 -0
  197. package/build/src/protocol/operations.d.ts +24 -12
  198. package/build/src/protocol/operations.js +36 -15
  199. package/build/src/protocol/placement.d.ts +2 -1
  200. package/build/src/protocol/placement.js +21 -5
  201. package/build/src/protocol/progress.d.ts +1 -1
  202. package/build/src/protocol/read/status.js +58 -41
  203. package/build/src/protocol/reintegrate.d.ts +1 -1
  204. package/build/src/protocol/reintegrate.js +10 -19
  205. package/build/src/protocol/review.d.ts +3 -28
  206. package/build/src/protocol/review.js +10 -37
  207. package/build/src/protocol/run.d.ts +19 -7
  208. package/build/src/protocol/run.js +19 -9
  209. package/build/src/runtime/proc/windows-launch.exe +0 -0
  210. package/build/src/settlement/holder.d.ts +2 -4
  211. package/build/src/settlement/holder.js +3 -6
  212. package/build/src/task/board.d.ts +6 -0
  213. package/build/src/task/board.js +30 -1
  214. package/build/src/task/catalog.js +1 -4
  215. package/build/src/task/compose.d.ts +0 -1
  216. package/build/src/task/compose.js +0 -1
  217. package/build/src/task/context.d.ts +0 -1
  218. package/build/src/task/context.js +0 -3
  219. package/build/src/task/created-observation.d.ts +5 -1
  220. package/build/src/task/created-observation.js +12 -5
  221. package/build/src/task/index.d.ts +1 -1
  222. package/build/src/task/index.js +7 -6
  223. package/build/src/task/input.d.ts +0 -6
  224. package/build/src/task/input.js +2 -14
  225. package/build/src/task/mutation.d.ts +3 -4
  226. package/build/src/task/mutation.js +11 -39
  227. package/build/src/task/operations.d.ts +2 -7
  228. package/build/src/task/operations.js +37 -49
  229. package/build/src/task/query.d.ts +0 -1
  230. package/build/src/task/query.js +1 -4
  231. package/build/src/task/store.d.ts +0 -2
  232. package/build/src/task/store.js +27 -11
  233. package/build/src/verification/declaration.js +3 -2
  234. package/build/src/verification/execution.d.ts +3 -2
  235. package/build/src/verification/execution.js +10 -6
  236. package/build/src/workspace-place.d.ts +0 -1
  237. package/build/src/workspace-place.js +0 -11
  238. package/package.json +13 -9
  239. package/build/integrations/marketplace/.claude-plugin/marketplace.json +0 -5
  240. package/build/integrations/marketplace/plugins/keiyaku/package.json +0 -17
  241. /package/build/{integrations/marketplace/plugins/keiyaku → plugins/claude}/skills/keiyaku/SKILL.md +0 -0
  242. /package/build/{integrations/marketplace/plugins/keiyaku → plugins/claude}/skills/keiyaku-akuma/references/automation.md +0 -0
  243. /package/build/{integrations/marketplace/plugins/keiyaku → plugins/claude}/skills/keiyaku-babysit/SKILL.md +0 -0
  244. /package/build/{integrations/marketplace/plugins/keiyaku → plugins/claude}/skills/keiyaku-bind/agents/openai.yaml +0 -0
  245. /package/build/{integrations/marketplace/plugins/keiyaku → plugins/claude}/skills/keiyaku-task/SKILL.md +0 -0
  246. /package/build/{integrations/marketplace/plugins/keiyaku/opencode.js → plugins/opencode/index.js} +0 -0
  247. /package/build/{integrations → plugins}/pi/keiyaku.ts +0 -0
@@ -3,13 +3,15 @@ import { z } from "zod";
3
3
  import { AuthorityCorruptionError } from "../core/facts/errors.js";
4
4
  import { gitObjectId, gitObjectIdForSnapshot, mintChangeId, mintSnapshotId } from "./identity.js";
5
5
  import { decodeGitNameOnly, decodeGitNumstat, readRef } from "./repository.js";
6
- import { GitPlumbingError, runGit, runGitPipe, runGitWithEnvironment } from "./process.js";
6
+ import { executeGit, GitPlumbingError, runGitPipe } from "./process.js";
7
+ import { deliveryEnvironment, materializeSnapshotCommit, } from "./tender.js";
7
8
  import { workspaceMergeStatePresent } from "./workspace.js";
9
+ import { withGitDecodeChannel } from "./read-observation.js";
8
10
  export { workspaceMergeStatePresent };
9
11
  const REQUIRED_GIT = "2.38";
10
12
  async function runAllowingNonzero(repository, args, input) {
11
13
  try {
12
- return { stdout: await runGit(repository, args, input), stderr: Buffer.alloc(0), status: 0 };
14
+ return { stdout: await executeGit(repository, args, input), stderr: Buffer.alloc(0), status: 0 };
13
15
  }
14
16
  catch (error) {
15
17
  if (!(error instanceof GitPlumbingError))
@@ -76,22 +78,34 @@ export async function worktreeChangeId(repository, input, tender) {
76
78
  const id = result.toString("utf8").trim().split(/\s/, 1)[0] ?? "";
77
79
  return mintChangeId(id === "" ? "0000000000000000000000000000000000000000" : id);
78
80
  }
81
+ /** Reuse only recorded commits with this exact tree and sole predecessor. */
82
+ export async function reusableIntegrationSnapshot(repository, tree, parent, recordedSnapshots) {
83
+ if (recordedSnapshots.length === 0)
84
+ return undefined;
85
+ return await withGitDecodeChannel(repository, async (channel) => {
86
+ for (const snapshot of recordedSnapshots) {
87
+ const oid = gitObjectIdForSnapshot(snapshot);
88
+ const object = (await channel.readObjects([oid])).get(oid);
89
+ if (object.kind === "missing")
90
+ continue;
91
+ if (object.type !== "commit") {
92
+ throw new AuthorityCorruptionError(`recorded integration snapshot is not a Git commit: ${snapshot}`);
93
+ }
94
+ const headers = object.bytes.toString("utf8").split("\n\n", 1)[0].split("\n");
95
+ const parents = headers.filter((line) => line.startsWith("parent "));
96
+ if (headers[0] === `tree ${tree}` && parents.length === 1 && parents[0] === `parent ${parent}`) {
97
+ return snapshot;
98
+ }
99
+ }
100
+ return undefined;
101
+ });
102
+ }
79
103
  /** Materialize the exact integration tree against its observed predecessor. */
80
104
  export async function materializeIntegrationSnapshot(repository, tree, parent, metadata) {
81
- const commit = (await runGitWithEnvironment(repository, ["commit-tree", tree, "-p", gitObjectIdForSnapshot(parent)], metadata.message, {
82
- GIT_AUTHOR_NAME: metadata.identity.name,
83
- GIT_AUTHOR_EMAIL: metadata.identity.email,
84
- GIT_COMMITTER_NAME: metadata.identity.name,
85
- GIT_COMMITTER_EMAIL: metadata.identity.email,
86
- GIT_AUTHOR_DATE: metadata.at,
87
- GIT_COMMITTER_DATE: metadata.at,
88
- }))
89
- .toString("utf8")
90
- .trim();
91
- return mintSnapshotId(commit);
105
+ return await materializeSnapshotCommit(repository, tree, [parent], metadata.message, deliveryEnvironment(metadata));
92
106
  }
93
107
  export async function persistedTender(repository, snapshot) {
94
- const tree = (await runGit(repository, ["show", "-s", "--format=%T", gitObjectIdForSnapshot(snapshot)]))
108
+ const tree = (await executeGit(repository, ["show", "-s", "--format=%T", gitObjectIdForSnapshot(snapshot)]))
95
109
  .toString("ascii")
96
110
  .trim();
97
111
  return { head: snapshot, tree: gitObjectId(tree, "persisted tender tree") };
@@ -100,7 +114,7 @@ export async function materializeReintegrationSnapshot(repository, tree, parent,
100
114
  const templateObject = gitObjectIdForSnapshot(template);
101
115
  let raw;
102
116
  try {
103
- raw = await runGit(repository, ["cat-file", "commit", templateObject]);
117
+ raw = await executeGit(repository, ["cat-file", "commit", templateObject]);
104
118
  }
105
119
  catch (error) {
106
120
  if (error instanceof GitPlumbingError) {
@@ -114,7 +128,7 @@ export async function materializeReintegrationSnapshot(repository, tree, parent,
114
128
  const message = raw.subarray(separator + 2);
115
129
  let fields;
116
130
  try {
117
- const formatted = (await runGit(repository, ["show", "-s", "--format=%an%x00%ae%x00%aI%x00%cn%x00%ce%x00%cI", templateObject]))
131
+ const formatted = (await executeGit(repository, ["show", "-s", "--format=%an%x00%ae%x00%aI%x00%cn%x00%ce%x00%cI", templateObject]))
118
132
  .toString("utf8")
119
133
  .replace(/\n$/u, "");
120
134
  fields = formatted.split("\0");
@@ -129,17 +143,14 @@ export async function materializeReintegrationSnapshot(repository, tree, parent,
129
143
  throw new AuthorityCorruptionError(`recorded delivery snapshot metadata is malformed: ${template}`);
130
144
  }
131
145
  const [authorName, authorEmail, authorDate, committerName, committerEmail, committerDate] = fields;
132
- const commit = (await runGitWithEnvironment(repository, ["commit-tree", tree, "-p", gitObjectIdForSnapshot(parent)], message, {
146
+ return await materializeSnapshotCommit(repository, tree, [parent], message, {
133
147
  GIT_AUTHOR_NAME: authorName,
134
148
  GIT_AUTHOR_EMAIL: authorEmail,
135
149
  GIT_AUTHOR_DATE: authorDate,
136
150
  GIT_COMMITTER_NAME: committerName,
137
151
  GIT_COMMITTER_EMAIL: committerEmail,
138
152
  GIT_COMMITTER_DATE: committerDate,
139
- }))
140
- .toString("utf8")
141
- .trim();
142
- return mintSnapshotId(commit);
153
+ });
143
154
  }
144
155
  async function isAncestor(repository, ancestor, descendant) {
145
156
  const result = await runAllowingNonzero(repository, [
@@ -302,7 +313,7 @@ export async function planIntegration(repository, input, tender, requireBranches
302
313
  }
303
314
  async function deliverySnapshotAvailability(repository, predecessor, candidate) {
304
315
  const objects = [gitObjectIdForSnapshot(predecessor), gitObjectIdForSnapshot(candidate)];
305
- const types = (await runGit(repository, ["cat-file", "--batch-check=%(objectname) %(objecttype)"], `${objects.join("\n")}\n`))
316
+ const types = (await executeGit(repository, ["cat-file", "--batch-check=%(objectname) %(objecttype)"], `${objects.join("\n")}\n`))
306
317
  .toString("ascii")
307
318
  .trimEnd()
308
319
  .split("\n")
@@ -323,7 +334,7 @@ export const deliveryDiffScopeSchema = z
323
334
  })
324
335
  .strict();
325
336
  export async function readDeliveryScope(repository, predecessor, candidate, includePaths) {
326
- const scope = decodeGitNumstat(await runGit(repository, [
337
+ const scope = decodeGitNumstat(await executeGit(repository, [
327
338
  "diff",
328
339
  "--numstat",
329
340
  "-z",
@@ -334,7 +345,7 @@ export async function readDeliveryScope(repository, predecessor, candidate, incl
334
345
  return scope;
335
346
  return {
336
347
  ...scope,
337
- paths: decodeGitNameOnly(await runGit(repository, [
348
+ paths: decodeGitNameOnly(await executeGit(repository, [
338
349
  "diff",
339
350
  "--name-only",
340
351
  "--no-renames",
@@ -349,7 +360,7 @@ export async function readDeliveryDiff(repository, predecessor, candidate) {
349
360
  if ((await deliverySnapshotAvailability(repository, predecessor, candidate)) === "unavailable")
350
361
  return null;
351
362
  try {
352
- return (await runGit(repository, [
363
+ return (await executeGit(repository, [
353
364
  "diff",
354
365
  "--no-ext-diff",
355
366
  "--no-color",
@@ -1,7 +1,7 @@
1
1
  import { access, lstat, realpath, rm } from "node:fs/promises";
2
2
  import { isAbsolute, relative, sep } from "node:path";
3
3
  import { nukeEmptyPlaceAuthority, readPlaceRegister, releaseManagedWorktrees, withPlaceAuthorityFence, } from "../workspace-place.js";
4
- import { GitPlumbingError, runGit } from "./process.js";
4
+ import { GitPlumbingError, executeGit } from "./process.js";
5
5
  import { confirmPrivateStatePublication, withPrivateStatePublicationSeat, } from "./private-state-seat.js";
6
6
  import { CANDIDATE_PIN_REF_NAMESPACE, DELIVERY_REF_NAMESPACE, GIT_REF, NoGitWorldError, readRef, registeredWorktrees, repositoryAt, worktreeGitDirectory, } from "./repository.js";
7
7
  import { worktreePath } from "./workspace.js";
@@ -89,7 +89,7 @@ async function removeManagedWorktree(repository, entry) {
89
89
  return await removeUnregisteredResidue(repository, entry);
90
90
  if (registered.branch !== null)
91
91
  return false;
92
- await runGit(repository, ["worktree", "remove", "--force", entry.path]);
92
+ await executeGit(repository, ["worktree", "remove", "--force", entry.path]);
93
93
  if ((await registeredWorktrees(repository)).some((candidate) => candidate.path === entry.path)) {
94
94
  throw new GitResetStopError(`managed Place worktree remains registered: ${entry.path}`);
95
95
  }
@@ -104,7 +104,7 @@ async function removeManagedWorktree(repository, entry) {
104
104
  throw new GitResetStopError(`managed Place worktree remains present: ${entry.path}`);
105
105
  }
106
106
  async function deleteRefAt(repository, ref, expectedOid) {
107
- await runGit(repository, ["update-ref", "--no-deref", "-d", ref, expectedOid]);
107
+ await executeGit(repository, ["update-ref", "--no-deref", "-d", ref, expectedOid]);
108
108
  }
109
109
  async function deleteObservedStateRef(repository, expectedOid, seat) {
110
110
  try {
@@ -126,7 +126,7 @@ async function removeOwnedRefs(repository, onRemoved) {
126
126
  let removed = 0;
127
127
  const roots = [DELIVERY_REF_NAMESPACE, CANDIDATE_PIN_REF_NAMESPACE];
128
128
  for (const root of roots) {
129
- const refs = (await runGit(repository, ["for-each-ref", "--format=%(refname)", root]))
129
+ const refs = (await executeGit(repository, ["for-each-ref", "--format=%(refname)", root]))
130
130
  .toString("utf8")
131
131
  .split("\n")
132
132
  .filter((ref) => ref.length > root.length && ref.startsWith(`${root}/`));
@@ -3,7 +3,7 @@ import type { ContractsObservation } from "../core/facts/observation.js";
3
3
  import { type GitSnapshot, type GitOid } from "./repository.js";
4
4
  import { type GitRepository } from "./process.js";
5
5
  import { type GitDecodeChannel, type GitReadObservation, type GitTreeSelection } from "./read-observation.js";
6
- export type TargetObservation = Readonly<{
6
+ type TargetObservation = Readonly<{
7
7
  head: SnapshotId | null;
8
8
  drift: boolean;
9
9
  }>;
@@ -12,11 +12,11 @@ type GitJournalRecord = Readonly<{
12
12
  entries: readonly JournalEntry[];
13
13
  state: ContractState | null;
14
14
  }>;
15
- export type ContractWorldObservation = Readonly<{
15
+ type ContractWorldObservation = Readonly<{
16
16
  snapshot: SnapshotId | null;
17
17
  contracts: ReadonlyMap<ContractId, GitJournalRecord>;
18
18
  }>;
19
- export type ActiveContractWorldObservation = ContractWorldObservation & Readonly<{
19
+ type ActiveContractWorldObservation = ContractWorldObservation & Readonly<{
20
20
  eligibility: ContractsObservation;
21
21
  }>;
22
22
  export type GitAdmissionSnapshot = Readonly<{
@@ -34,7 +34,7 @@ export type BindCoordinatesObservation = Readonly<{
34
34
  target?: string;
35
35
  branch: string | null;
36
36
  }>;
37
- export type UnbornHeadObservation = Readonly<{
37
+ type UnbornHeadObservation = Readonly<{
38
38
  kind: "unborn-head";
39
39
  branch: string | null;
40
40
  }>;
@@ -3,7 +3,7 @@ import { AuthorityCorruptionError } from "../core/facts/errors.js";
3
3
  import { foldJournal } from "../core/facts/fold.js";
4
4
  import { contractJournalPath, contractJournalPaths, mintContractHead, mintSnapshotId, } from "./identity.js";
5
5
  import { isKeiyakuOwnedRef } from "./repository.js";
6
- import { GitPlumbingError, runGit } from "./process.js";
6
+ import { GitPlumbingError, executeGit } from "./process.js";
7
7
  import { readBlobResults, readGitTreeSelection, withGitTargetedReadObservation, } from "./read-observation.js";
8
8
  export async function observeDeliveryTargetAt(observation, state) {
9
9
  const target = state.coordinates.target;
@@ -29,7 +29,7 @@ export async function normalizeTargetBranch(repository, input) {
29
29
  if (branch.length === 0 || branch.startsWith("-") || isKeiyakuOwnedRef(target))
30
30
  return null;
31
31
  try {
32
- const checked = (await runGit(repository, ["check-ref-format", "--branch", branch])).toString("utf8").trim();
32
+ const checked = (await executeGit(repository, ["check-ref-format", "--branch", branch])).toString("utf8").trim();
33
33
  return checked === branch ? target : null;
34
34
  }
35
35
  catch (error) {
@@ -41,7 +41,7 @@ export async function normalizeTargetBranch(repository, input) {
41
41
  /** Read one worktree's attached branch, or null for detached HEAD. */
42
42
  export async function currentBranch(repository, path) {
43
43
  try {
44
- const ref = (await runGit(repository, [...(path === undefined ? [] : ["-C", path]), "symbolic-ref", "--quiet", "HEAD"]))
44
+ const ref = (await executeGit(repository, [...(path === undefined ? [] : ["-C", path]), "symbolic-ref", "--quiet", "HEAD"]))
45
45
  .toString("utf8")
46
46
  .trim();
47
47
  return ref.startsWith("refs/heads/") ? ref : null;
@@ -64,7 +64,7 @@ function structuredFields(output, fieldCount) {
64
64
  async function observeHeadCoordinates(repository) {
65
65
  try {
66
66
  return {
67
- start: mintSnapshotId((await runGit(repository, ["rev-parse", "--verify", "HEAD"])).toString("utf8").trim()),
67
+ start: mintSnapshotId((await executeGit(repository, ["rev-parse", "--verify", "HEAD"])).toString("utf8").trim()),
68
68
  branch: await currentBranch(repository),
69
69
  };
70
70
  }
@@ -84,7 +84,7 @@ export async function observeBindCoordinates(repository, selection = { kind: "ta
84
84
  if (isKeiyakuOwnedRef(requestedTarget)) {
85
85
  throw new Error(`bind target names a Keiyaku-owned ref: ${requestedTarget}`);
86
86
  }
87
- const output = await runGit(repository, [
87
+ const output = await executeGit(repository, [
88
88
  "for-each-ref",
89
89
  `--format=${TARGET_COORDINATES_FORMAT}`,
90
90
  "--",
@@ -16,8 +16,6 @@ export type GitRepository = Readonly<{
16
16
  /** Test-only hook after the private-state seat close attempt. */
17
17
  readonly onPrivateStateSeatClose?: () => void;
18
18
  }>;
19
- /** Give one caller's Git capability view its cancellation boundary. */
20
- export declare function withGitAbortSignal(repository: GitRepository, signal: AbortSignal | undefined): GitRepository;
21
19
  export declare class GitPlumbingError extends Error {
22
20
  readonly stdout: Buffer;
23
21
  readonly stderr: Buffer;
@@ -31,8 +29,7 @@ export declare class GitPlumbingError extends Error {
31
29
  pid?: number | null;
32
30
  }>);
33
31
  }
34
- export declare function runGit(repository: GitRepository, args: readonly string[], input?: string | Uint8Array): Promise<Buffer>;
32
+ export declare function executeGit(repository: GitRepository, args: readonly string[], input?: string | Uint8Array, environment?: NodeJS.ProcessEnv): Promise<Buffer>;
35
33
  /** Connect two owned Git commands without materializing the intermediate output. */
36
34
  export declare function runGitPipe(repository: GitRepository, sourceArgs: readonly string[], sinkArgs: readonly string[]): Promise<Buffer>;
37
35
  export declare function consumeGitStdout(repository: GitRepository, args: readonly string[], consume: (chunk: Buffer) => void): Promise<void>;
38
- export declare function runGitWithEnvironment(repository: GitRepository, args: readonly string[], input: string | Uint8Array | undefined, environment: NodeJS.ProcessEnv): Promise<Buffer>;
@@ -1,9 +1,5 @@
1
1
  import { consumeProcessStdout, spawnCancellableProcess } from "../runtime/proc/run.js";
2
2
  import { pipeline } from "node:stream/promises";
3
- /** Give one caller's Git capability view its cancellation boundary. */
4
- export function withGitAbortSignal(repository, signal) {
5
- return signal === undefined ? repository : { ...repository, signal };
6
- }
7
3
  export class GitPlumbingError extends Error {
8
4
  stdout;
9
5
  stderr;
@@ -32,7 +28,7 @@ function commandError(command, error) {
32
28
  pid: candidate.pid ?? null,
33
29
  });
34
30
  }
35
- async function executeGit(repository, args, input, environment) {
31
+ export async function executeGit(repository, args, input, environment) {
36
32
  if (repository.signal?.aborted === true) {
37
33
  throw commandError(args, { message: "git process cancelled", status: null });
38
34
  }
@@ -81,9 +77,6 @@ async function executeGit(repository, args, input, environment) {
81
77
  pid: child.pid,
82
78
  });
83
79
  }
84
- export async function runGit(repository, args, input) {
85
- return await executeGit(repository, args, input);
86
- }
87
80
  function boundedPipeOutput(child, args) {
88
81
  const stdout = [];
89
82
  let bytes = 0;
@@ -212,6 +205,3 @@ export async function consumeGitStdout(repository, args, consume) {
212
205
  pid: result.pid ?? undefined,
213
206
  });
214
207
  }
215
- export async function runGitWithEnvironment(repository, args, input, environment) {
216
- return await executeGit(repository, args, input, environment);
217
- }
@@ -6,7 +6,7 @@ import { GIT_FORMAT_BYTES, GIT_FORMAT_PATH, GIT_REF, readRef } from "./repositor
6
6
  import { GitPlumbingError } from "./process.js";
7
7
  const gitReadObservationBrand = Symbol("GitReadObservation");
8
8
  const gitDecodeChannelBrand = Symbol("GitDecodeChannel");
9
- const BATCH_RETIREMENT_GRACE_MS = 1_000;
9
+ const BATCH_RETIREMENT_GRACE_MS = 5_000;
10
10
  function streamCursor(stream) {
11
11
  const iterator = stream[Symbol.asyncIterator]();
12
12
  let buffer = Buffer.alloc(0);
@@ -142,7 +142,7 @@ type ReconcileBatchItem = Readonly<{
142
142
  state: ContractState | null;
143
143
  result: ReconcileResult;
144
144
  }>;
145
- export type GitReconcileObservation = Readonly<{
145
+ type GitReconcileObservation = Readonly<{
146
146
  state: ContractState | null;
147
147
  result: ReconcileResult;
148
148
  }>;
@@ -8,7 +8,7 @@ import { dirname, join } from "node:path";
8
8
  import { acquireSqliteTransactionLock, SqliteTransactionLockError, } from "../coordination/sqlite-transaction-lock.js";
9
9
  import { AuthorityCorruptionError } from "../core/facts/errors.js";
10
10
  import { CANDIDATE_PIN_REF_NAMESPACE, commonGitDirectory, DELIVERY_REF_NAMESPACE, registeredWorktreePaths, } from "./repository.js";
11
- import { runGit, GitPlumbingError } from "./process.js";
11
+ import { executeGit, GitPlumbingError } from "./process.js";
12
12
  import { contractLocator, contractPhysicalName, gitObjectIdForSnapshot } from "./identity.js";
13
13
  import { observeContractAt } from "./observe.js";
14
14
  import { acquireTargetPlacementFence, recoverTargetPlacement } from "./target-placement.js";
@@ -158,25 +158,27 @@ async function worktree(repository, topology, path, desired) {
158
158
  if (registered && (await pathExists(path)))
159
159
  return { kind: "worktree", path, action: "unchanged" };
160
160
  if (registered) {
161
- await runGit(repository, ["worktree", "remove", path]);
161
+ await executeGit(repository, ["worktree", "remove", path]);
162
162
  topology.paths.delete(path);
163
163
  }
164
164
  if (await pathExists(path))
165
165
  throw new WorktreeCustodyError(`delivery worktree path is occupied: ${path}`);
166
166
  await mkdir(dirname(path), { recursive: true });
167
- await runGit(repository, ["worktree", "add", "--detach", path, gitObjectIdForSnapshot(desired)]);
167
+ await executeGit(repository, ["worktree", "add", "--detach", path, gitObjectIdForSnapshot(desired)]);
168
168
  topology.paths.add(path);
169
169
  return { kind: "worktree", path, action: "created" };
170
170
  }
171
- async function removeCollectableScratch(repository, topology, acc) {
172
- for (const removal of await removeCollectableScratchWorktrees(repository, topology.paths)) {
173
- acc.recordEffect({ kind: "worktree", path: removal.path, action: removal.action });
174
- if (removal.retained)
175
- acc.recordLag({
176
- kind: "worktree-retained",
177
- path: removal.path,
178
- ...(removal.diagnostic === undefined ? {} : { diagnostic: removal.diagnostic }),
179
- });
171
+ async function removeCollectableScratch(repository, topology, acc, terminalContract) {
172
+ for (const removal of await removeCollectableScratchWorktrees(repository, topology.paths, terminalContract)) {
173
+ if (!removal.retained) {
174
+ acc.recordEffect({ kind: "worktree", path: removal.path, action: "removed" });
175
+ continue;
176
+ }
177
+ acc.recordLag({
178
+ kind: "worktree-retained",
179
+ path: removal.path,
180
+ ...(removal.diagnostic === undefined ? {} : { diagnostic: removal.diagnostic }),
181
+ });
180
182
  }
181
183
  }
182
184
  async function reconcileTargetCheckouts(repository, state, onPhysical) {
@@ -288,7 +290,7 @@ async function reconcileWithTopology(input, state, topology) {
288
290
  const acc = reconcileAccumulation(input.onPhysical);
289
291
  const { effects, lag, hookRuns } = acc;
290
292
  try {
291
- await removeCollectableScratch(repository, topology, acc);
293
+ await removeCollectableScratch(repository, topology, acc, state?.terminal ? state.id : undefined);
292
294
  if (!state)
293
295
  return complete(effects, lag);
294
296
  const targetCheckouts = await reconcileTargetCheckouts(repository, state, input.onPhysical);
@@ -7,7 +7,6 @@ export declare const CANDIDATE_PIN_REF_NAMESPACE = "refs/keiyaku/candidate";
7
7
  export declare const HANDOFF_RECEIPT_REF_NAMESPACE = "refs/keiyaku/handoff";
8
8
  export declare const GIT_FORMAT_PATH = "meta/format.json";
9
9
  export declare const GIT_FORMAT_BYTES = "{\"version\":4}\n";
10
- export declare const STATE_COMMIT_SUBJECT = "keiyaku authority - do not delete or rewrite";
11
10
  export declare function isKeiyakuOwnedRef(ref: string): boolean;
12
11
  export type GitOid = string;
13
12
  export type RefPublication = Readonly<{
@@ -31,7 +30,7 @@ export declare class NoGitWorldError extends Error {
31
30
  readonly path: string;
32
31
  constructor(path: string);
33
32
  }
34
- export type RegisteredWorktree = Readonly<{
33
+ type RegisteredWorktree = Readonly<{
35
34
  path: string;
36
35
  branch: string | null;
37
36
  }>;
@@ -40,12 +39,9 @@ export declare function registeredWorktreePaths(repository: GitRepository): Prom
40
39
  export declare function repositoryAt(cwd: string, gitPath?: string): Promise<GitRepository>;
41
40
  export declare function commonGitDirectory(repository: GitRepository): string;
42
41
  export declare function worktreeGitDirectory(repository: GitRepository, worktree: string): Promise<string>;
43
- export declare function worktreeRoot(repository: GitRepository): Promise<string>;
44
42
  export declare function readRef(repository: GitRepository, ref: string): Promise<GitOid | null>;
45
43
  /** Read an explicit set of asserted refs from one fresh Git observation. */
46
44
  export declare function readRefs(repository: GitRepository, refs: readonly string[]): Promise<Map<any, any>>;
47
- export declare function readTreeEntries(repository: GitRepository, tree: GitOid): Promise<Map<string, TreeEntry>>;
48
- export declare function readGit(repository: GitRepository): Promise<GitSnapshot>;
49
45
  /** Read only the requested private Git paths from one immutable Git tree. */
50
46
  export declare function readGitPaths(repository: GitRepository, requestedPaths: readonly string[]): Promise<GitSnapshot>;
51
47
  export declare function writeBlob(repository: GitRepository, bytes: string | Uint8Array): Promise<GitOid>;
@@ -74,7 +70,7 @@ export declare function updateRefsAtomically(repository: GitRepository, updates:
74
70
  readonly newOid: GitOid;
75
71
  readonly expectedOid: GitOid;
76
72
  })[], assertions?: readonly GitRefAssertion[]): Promise<RefPublication>;
77
- export type GitDiffStat = Readonly<{
73
+ type GitDiffStat = Readonly<{
78
74
  filesChanged: number;
79
75
  insertions: number;
80
76
  deletions: number;
@@ -2,7 +2,7 @@ import { isAbsolute, resolve } from "node:path";
2
2
  import { realpath } from "node:fs/promises";
3
3
  import { AuthorityCorruptionError } from "../core/facts/errors.js";
4
4
  import { gitObjectId } from "./identity.js";
5
- import { GitPlumbingError, runGit, runGitWithEnvironment } from "./process.js";
5
+ import { executeGit, GitPlumbingError } from "./process.js";
6
6
  import { parseTreeObject, prepareTreeUpdate, treeUpdate, validPath, } from "./tree.js";
7
7
  export const GIT_REF = "refs/heads/keiyaku-state";
8
8
  export const DELIVERY_REF_NAMESPACE = "refs/keiyaku/delivery";
@@ -11,7 +11,7 @@ export const HANDOFF_RECEIPT_REF_NAMESPACE = "refs/keiyaku/handoff";
11
11
  export const GIT_FORMAT_PATH = "meta/format.json";
12
12
  const CURRENT_FORMAT_VERSION = 4;
13
13
  export const GIT_FORMAT_BYTES = `{"version":${CURRENT_FORMAT_VERSION}}\n`;
14
- export const STATE_COMMIT_SUBJECT = "keiyaku authority - do not delete or rewrite";
14
+ const STATE_COMMIT_SUBJECT = "keiyaku authority - do not delete or rewrite";
15
15
  export function isKeiyakuOwnedRef(ref) {
16
16
  return (ref === GIT_REF ||
17
17
  ref === DELIVERY_REF_NAMESPACE ||
@@ -67,7 +67,7 @@ function worktreesFromPorcelain(output) {
67
67
  return worktrees;
68
68
  }
69
69
  export async function registeredWorktrees(repository) {
70
- return await Promise.all(worktreesFromPorcelain(await runGit(repository, ["worktree", "list", "--porcelain", "-z"])).map(async (worktree) => {
70
+ return await Promise.all(worktreesFromPorcelain(await executeGit(repository, ["worktree", "list", "--porcelain", "-z"])).map(async (worktree) => {
71
71
  const { prunable, ...registered } = worktree;
72
72
  try {
73
73
  return { ...registered, path: await realpath(registered.path) };
@@ -111,7 +111,9 @@ export async function repositoryAt(cwd, gitPath = "git") {
111
111
  return { gitPath, effectiveCwd, invocationWorktree, primaryWorktree, commonDirectory };
112
112
  }
113
113
  async function absoluteGitPath(repository, args, label) {
114
- const value = (await runGit(repository, ["rev-parse", "--path-format=absolute", ...args])).toString("utf8").trim();
114
+ const value = (await executeGit(repository, ["rev-parse", "--path-format=absolute", ...args]))
115
+ .toString("utf8")
116
+ .trim();
115
117
  if (value.length === 0)
116
118
  throw new Error(`${label} is empty`);
117
119
  return await realpath(resolve(isAbsolute(value) ? value : resolve(repository.effectiveCwd, value)));
@@ -123,7 +125,7 @@ export async function worktreeGitDirectory(repository, worktree) {
123
125
  const scoped = { ...repository, effectiveCwd: worktree };
124
126
  return await absoluteGitPath(scoped, ["--git-dir"], "worktree Git directory");
125
127
  }
126
- export async function worktreeRoot(repository) {
128
+ async function worktreeRoot(repository) {
127
129
  return await absoluteGitPath(repository, ["--show-toplevel"], "worktree root");
128
130
  }
129
131
  function assertRef(ref) {
@@ -138,7 +140,7 @@ function assertAssertionRef(ref) {
138
140
  export async function readRef(repository, ref) {
139
141
  assertAssertionRef(ref);
140
142
  try {
141
- const oid = (await runGit(repository, ["rev-parse", "--verify", "--quiet", ref])).toString("utf8").trim();
143
+ const oid = (await executeGit(repository, ["rev-parse", "--verify", "--quiet", ref])).toString("utf8").trim();
142
144
  return oid.length === 0 ? null : gitObjectId(oid, `ref ${ref}`);
143
145
  }
144
146
  catch (error) {
@@ -154,7 +156,7 @@ export async function readRefs(repository, refs) {
154
156
  assertAssertionRef(ref);
155
157
  if (refsToRead.length === 0)
156
158
  return new Map();
157
- const output = (await runGit(repository, ["cat-file", "--batch-check=%(objectname)"], `${refsToRead.join("\n")}\n`)).toString("utf8");
159
+ const output = (await executeGit(repository, ["cat-file", "--batch-check=%(objectname)"], `${refsToRead.join("\n")}\n`)).toString("utf8");
158
160
  const records = output.split("\n");
159
161
  if (records.pop() !== "" || records.length !== refsToRead.length)
160
162
  throw new Error("Git ref batch output is malformed");
@@ -168,7 +170,7 @@ export async function readRefs(repository, refs) {
168
170
  }
169
171
  async function readTreeForCommit(repository, commit) {
170
172
  gitObjectId(commit, "Git commit");
171
- const tree = (await runGit(repository, ["show", "-s", "--format=%T", commit])).toString("utf8").trim();
173
+ const tree = (await executeGit(repository, ["show", "-s", "--format=%T", commit])).toString("utf8").trim();
172
174
  gitObjectId(tree, "Git tree");
173
175
  return tree;
174
176
  }
@@ -190,21 +192,6 @@ function parseTreeEntries(output) {
190
192
  }
191
193
  return entries;
192
194
  }
193
- export async function readTreeEntries(repository, tree) {
194
- gitObjectId(tree, "tree");
195
- const output = await runGit(repository, ["ls-tree", "-r", "-z", "--full-tree", tree]);
196
- return parseTreeEntries(output);
197
- }
198
- export async function readGit(repository) {
199
- const commit = await readRef(repository, GIT_REF);
200
- if (commit === null) {
201
- return { commit: null, tree: null, paths: new Map() };
202
- }
203
- const tree = await readTreeForCommit(repository, commit);
204
- const paths = await readTreeEntries(repository, tree);
205
- await validateGitFormat(repository, paths);
206
- return { commit, tree, paths };
207
- }
208
195
  /** Read only the requested private Git paths from one immutable Git tree. */
209
196
  export async function readGitPaths(repository, requestedPaths) {
210
197
  for (const path of requestedPaths)
@@ -213,18 +200,18 @@ export async function readGitPaths(repository, requestedPaths) {
213
200
  if (commit === null)
214
201
  return { commit: null, tree: null, paths: new Map() };
215
202
  const tree = await readTreeForCommit(repository, commit);
216
- const paths = parseTreeEntries(await runGit(repository, ["ls-tree", "-z", "--full-tree", tree, "--", GIT_FORMAT_PATH, ...requestedPaths]));
203
+ const paths = parseTreeEntries(await executeGit(repository, ["ls-tree", "-z", "--full-tree", tree, "--", GIT_FORMAT_PATH, ...requestedPaths]));
217
204
  await validateGitFormat(repository, paths);
218
205
  return { commit, tree, paths };
219
206
  }
220
207
  export async function writeBlob(repository, bytes) {
221
- const oid = (await runGit(repository, ["hash-object", "-w", "--stdin"], bytes)).toString("utf8").trim();
208
+ const oid = (await executeGit(repository, ["hash-object", "-w", "--stdin"], bytes)).toString("utf8").trim();
222
209
  gitObjectId(oid, "written blob");
223
210
  return oid;
224
211
  }
225
212
  export async function readBlob(repository, oid) {
226
213
  gitObjectId(oid, "blob");
227
- return await runGit(repository, ["cat-file", "blob", oid]);
214
+ return await executeGit(repository, ["cat-file", "blob", oid]);
228
215
  }
229
216
  function malformedBatchOutput(detail) {
230
217
  throw new GitPlumbingError({
@@ -239,7 +226,7 @@ async function readObjects(repository, oids) {
239
226
  gitObjectId(oid, "Git object");
240
227
  if (unique.length === 0)
241
228
  return new Map();
242
- const output = await runGit(repository, ["cat-file", "--batch"], `${unique.join("\n")}\n`);
229
+ const output = await executeGit(repository, ["cat-file", "--batch"], `${unique.join("\n")}\n`);
243
230
  const objects = new Map();
244
231
  let offset = 0;
245
232
  for (const oid of unique) {
@@ -290,7 +277,7 @@ async function baseTreeEntries(repository, baseTree, changedPaths, nodePaths) {
290
277
  const byPath = new Map();
291
278
  if (baseTree === null)
292
279
  return byPath;
293
- const ancestry = parseTreeEntries(await runGit(repository, [
280
+ const ancestry = parseTreeEntries(await executeGit(repository, [
294
281
  "ls-tree",
295
282
  "-z",
296
283
  "-t",
@@ -323,7 +310,7 @@ async function baseTreeEntries(repository, baseTree, changedPaths, nodePaths) {
323
310
  return byPath;
324
311
  }
325
312
  async function writePreparedTrees(repository, prepared) {
326
- const output = await runGit(repository, ["mktree", "--batch"], prepared.map(({ records }) => `${records}\n`).join(""));
313
+ const output = await executeGit(repository, ["mktree", "--batch"], prepared.map(({ records }) => `${records}\n`).join(""));
327
314
  const written = output.toString("ascii").trimEnd().split("\n");
328
315
  if (written.length !== prepared.length) {
329
316
  throw new GitPlumbingError({ stderr: output, status: null, message: "malformed mktree --batch output" });
@@ -364,7 +351,7 @@ async function writeCommitObject(input) {
364
351
  const args = ["commit-tree", tree];
365
352
  for (const parent of parents)
366
353
  args.push("-p", parent);
367
- const commit = (await runGitWithEnvironment(repository, args, `${message}\n`, {
354
+ const commit = (await executeGit(repository, args, `${message}\n`, {
368
355
  GIT_AUTHOR_NAME: actor,
369
356
  GIT_AUTHOR_EMAIL: email,
370
357
  GIT_COMMITTER_NAME: actor,
@@ -420,7 +407,7 @@ export async function updateRefsAtomically(repository, updates, assertions = [])
420
407
  }
421
408
  const lines = ["start", ...assertions.map(refAssertionLine), ...updates.map(refUpdateLine), "prepare", "commit", ""];
422
409
  try {
423
- await runGit(repository, ["update-ref", "--stdin"], lines.join("\n"));
410
+ await executeGit(repository, ["update-ref", "--stdin"], lines.join("\n"));
424
411
  return { kind: "published" };
425
412
  }
426
413
  catch (error) {
@@ -1,23 +1,23 @@
1
1
  import { z } from "zod";
2
- import type { SnapshotId } from "../core/facts/types.js";
2
+ import type { ContractId, SnapshotId } from "../core/facts/types.js";
3
3
  import { type GitRepository } from "./process.js";
4
4
  export type MaterializedScratchCandidate = Readonly<{
5
5
  cwd: string;
6
- dispose: () => Promise<WorktreeLeak | null>;
6
+ dispose: (retain?: boolean) => Promise<WorktreeLeak | null>;
7
7
  }>;
8
8
  export declare const worktreeLeakSchema: z.ZodObject<{
9
- path: z.ZodString;
9
+ path: z.ZodOptional<z.ZodString>;
10
10
  diagnostic: z.ZodString;
11
11
  }, z.core.$strict>;
12
12
  export type WorktreeLeak = z.infer<typeof worktreeLeakSchema>;
13
13
  type CollectableScratchRemoval = Readonly<{
14
14
  path: string;
15
- action: "removed" | "unchanged";
15
+ /** False only when Git removed the path and absence was confirmed; retained removals own a diagnostic. */
16
16
  retained: boolean;
17
17
  diagnostic?: string;
18
18
  }>;
19
19
  /** Remove only abandoned Verification scratch worktrees whose ownership locks are held. */
20
- export declare function removeCollectableScratchWorktrees(repository: GitRepository, paths: Set<string>): Promise<readonly CollectableScratchRemoval[]>;
20
+ export declare function removeCollectableScratchWorktrees(repository: GitRepository, paths: Set<string>, terminalContract?: ContractId): Promise<readonly CollectableScratchRemoval[]>;
21
21
  /** Materialize a disposable candidate worktree and own only its removal. */
22
- export declare function materializeScratchCandidate(repository: GitRepository, candidate: SnapshotId): Promise<MaterializedScratchCandidate>;
22
+ export declare function materializeScratchCandidate(repository: GitRepository, candidate: SnapshotId, contract?: ContractId): Promise<MaterializedScratchCandidate>;
23
23
  export {};