@astrosheep/keiyaku 4.1.0 → 4.1.2

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 (278) hide show
  1. package/README.md +1 -1
  2. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +3 -0
  3. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +3 -0
  4. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +3 -6
  5. package/build/src/akuma/akuma-errors.d.ts +6 -0
  6. package/build/src/akuma/akuma-errors.js +9 -0
  7. package/build/src/akuma/akuma-handle.d.ts +39 -0
  8. package/build/src/akuma/akuma-handle.js +263 -0
  9. package/build/src/akuma/akuma-observe.d.ts +19 -0
  10. package/build/src/akuma/akuma-observe.js +74 -0
  11. package/build/src/akuma/akuma-product-symbols.d.ts +1 -0
  12. package/build/src/akuma/akuma-product-symbols.js +1 -0
  13. package/build/src/akuma/akuma-product.d.ts +18 -0
  14. package/build/src/akuma/akuma-product.js +154 -0
  15. package/build/src/akuma/akuma.d.ts +37 -81
  16. package/build/src/akuma/akuma.js +39 -447
  17. package/build/src/akuma/allowed.d.ts +1 -0
  18. package/build/src/akuma/allowed.js +6 -0
  19. package/build/src/akuma/archetype.js +8 -5
  20. package/build/src/akuma/body-supervisor.d.ts +26 -0
  21. package/build/src/akuma/body-supervisor.js +89 -0
  22. package/build/src/akuma/body.d.ts +45 -4
  23. package/build/src/akuma/body.js +145 -35
  24. package/build/src/akuma/coordinate.js +17 -7
  25. package/build/src/akuma/heart/facts.js +1 -1
  26. package/build/src/akuma/heart/index.d.ts +1 -1
  27. package/build/src/akuma/heart/index.js +2 -4
  28. package/build/src/akuma/heart/request-authority.js +4 -4
  29. package/build/src/akuma/heart/request-rows.js +27 -16
  30. package/build/src/akuma/heart/rows.js +79 -41
  31. package/build/src/akuma/heart/soul.js +1 -1
  32. package/build/src/akuma/heart/storage.d.ts +1 -0
  33. package/build/src/akuma/heart/storage.js +76 -11
  34. package/build/src/akuma/heart/tells.js +35 -18
  35. package/build/src/akuma/heart/timeline.js +19 -11
  36. package/build/src/akuma/identity.d.ts +1 -0
  37. package/build/src/akuma/identity.js +1 -0
  38. package/build/src/akuma/index.d.ts +3 -3
  39. package/build/src/akuma/index.js +1 -1
  40. package/build/src/akuma/nuke.js +106 -14
  41. package/build/src/akuma/projection-read.d.ts +17 -0
  42. package/build/src/akuma/projection-read.js +135 -0
  43. package/build/src/akuma/projection.d.ts +1 -16
  44. package/build/src/akuma/projection.js +77 -154
  45. package/build/src/akuma/provider-recipe.js +11 -5
  46. package/build/src/akuma/provider.js +105 -62
  47. package/build/src/akuma/providers/acp/config.js +5 -6
  48. package/build/src/akuma/providers/acp/core.js +17 -10
  49. package/build/src/akuma/providers/acp/events.js +38 -29
  50. package/build/src/akuma/providers/acp/index.js +13 -9
  51. package/build/src/akuma/providers/claude/events.js +8 -12
  52. package/build/src/akuma/providers/claude/index.d.ts +1 -0
  53. package/build/src/akuma/providers/claude/index.js +55 -26
  54. package/build/src/akuma/providers/claude/input.js +6 -2
  55. package/build/src/akuma/providers/codex-app-server/events.js +61 -36
  56. package/build/src/akuma/providers/codex-app-server/index.js +25 -13
  57. package/build/src/akuma/providers/grok-build/index.js +22 -25
  58. package/build/src/akuma/providers/index.js +2 -18
  59. package/build/src/akuma/providers/opencode-sdk/events.js +32 -18
  60. package/build/src/akuma/providers/opencode-sdk/index.js +26 -17
  61. package/build/src/akuma/providers/opencode-sdk/session.js +21 -5
  62. package/build/src/akuma/providers/pi/events.js +26 -19
  63. package/build/src/akuma/providers/pi/index.js +58 -40
  64. package/build/src/akuma/publication.js +9 -9
  65. package/build/src/akuma/request-execution.d.ts +6 -0
  66. package/build/src/akuma/request-execution.js +86 -0
  67. package/build/src/akuma/request-serve.d.ts +12 -1
  68. package/build/src/akuma/request-serve.js +19 -122
  69. package/build/src/akuma/request-wire.d.ts +2 -2
  70. package/build/src/akuma/request-wire.js +75 -102
  71. package/build/src/akuma/requests.js +2 -2
  72. package/build/src/akuma/{body-turn.d.ts → turn-drive.d.ts} +2 -25
  73. package/build/src/akuma/{body-turn.js → turn-drive.js} +20 -103
  74. package/build/src/akuma-body.js +6 -5
  75. package/build/src/body/amend.d.ts +4 -1
  76. package/build/src/body/amend.js +14 -6
  77. package/build/src/body/decode.js +2 -4
  78. package/build/src/body/envelope.js +2 -4
  79. package/build/src/body/region.d.ts +1 -2
  80. package/build/src/body/region.js +2 -70
  81. package/build/src/body/render.js +17 -12
  82. package/build/src/body/shape.js +1 -7
  83. package/build/src/cli/accepted.d.ts +1 -1
  84. package/build/src/cli/accepted.js +8 -1
  85. package/build/src/cli/commands/akuma-invoke.js +18 -6
  86. package/build/src/cli/commands/akuma.d.ts +0 -6
  87. package/build/src/cli/commands/akuma.js +11 -24
  88. package/build/src/cli/commands/amend.d.ts +2 -2
  89. package/build/src/cli/commands/amend.js +2 -4
  90. package/build/src/cli/commands/bind.d.ts +4 -4
  91. package/build/src/cli/commands/bind.js +16 -6
  92. package/build/src/cli/commands/contract-invoke.d.ts +25 -0
  93. package/build/src/cli/commands/contract-invoke.js +193 -0
  94. package/build/src/cli/commands/contract.d.ts +109 -10
  95. package/build/src/cli/commands/contract.js +339 -16
  96. package/build/src/cli/commands/install.d.ts +1 -1
  97. package/build/src/cli/commands/install.js +23 -15
  98. package/build/src/cli/commands/task-invoke.js +187 -67
  99. package/build/src/cli/commands/task-query.js +48 -24
  100. package/build/src/cli/commands/task.js +150 -38
  101. package/build/src/cli/coordinates.js +2 -4
  102. package/build/src/cli/invoke.js +92 -208
  103. package/build/src/cli/main.js +4 -2
  104. package/build/src/cli/parse.d.ts +3 -90
  105. package/build/src/cli/parse.js +27 -233
  106. package/build/src/cli/render/akuma-activity.d.ts +38 -0
  107. package/build/src/cli/render/akuma-activity.js +362 -0
  108. package/build/src/cli/render/akuma-tool-command.js +6 -7
  109. package/build/src/cli/render/akuma-tool.js +10 -8
  110. package/build/src/cli/render/akuma.d.ts +1 -1
  111. package/build/src/cli/render/akuma.js +61 -353
  112. package/build/src/cli/render/audit.js +5 -15
  113. package/build/src/cli/render/board.d.ts +1 -0
  114. package/build/src/cli/render/board.js +17 -0
  115. package/build/src/cli/render/catalog.js +81 -8
  116. package/build/src/cli/render/contract-history.d.ts +2 -0
  117. package/build/src/cli/render/contract-history.js +83 -0
  118. package/build/src/cli/render/contract-observation.d.ts +10 -0
  119. package/build/src/cli/render/contract-observation.js +58 -0
  120. package/build/src/cli/render/contract.d.ts +1 -2
  121. package/build/src/cli/render/contract.js +41 -142
  122. package/build/src/cli/render/kanshi.js +239 -215
  123. package/build/src/cli/render/receipt.js +2 -9
  124. package/build/src/cli/render/refusal.js +10 -15
  125. package/build/src/cli/render/region.js +22 -6
  126. package/build/src/cli/render/settings.js +4 -4
  127. package/build/src/cli/render/task.js +34 -14
  128. package/build/src/cli/render/terminal.js +16 -5
  129. package/build/src/cli/result.d.ts +3 -3
  130. package/build/src/cli/runtime.d.ts +6 -1
  131. package/build/src/cli/runtime.js +84 -63
  132. package/build/src/cli/selectors.js +2 -4
  133. package/build/src/cli/usage.js +6 -5
  134. package/build/src/contract-worktree.d.ts +1 -23
  135. package/build/src/contract-worktree.js +79 -155
  136. package/build/src/coordination/durable-file.js +7 -3
  137. package/build/src/coordination/sqlite-transaction-lock.js +29 -12
  138. package/build/src/core/facts/codec.js +7 -3
  139. package/build/src/core/facts/types.d.ts +1 -1
  140. package/build/src/core/subject.js +16 -9
  141. package/build/src/core/verbs/abandon.d.ts +1 -1
  142. package/build/src/core/verbs/abandon.js +1 -1
  143. package/build/src/core/verbs/amend.d.ts +1 -1
  144. package/build/src/core/verbs/amend.js +7 -7
  145. package/build/src/core/verbs/attestation.js +4 -1
  146. package/build/src/core/verbs/bind.d.ts +1 -1
  147. package/build/src/core/verbs/bind.js +1 -1
  148. package/build/src/core/verbs/placement.d.ts +1 -1
  149. package/build/src/core/verbs/placement.js +10 -8
  150. package/build/src/dispatch/index.js +8 -6
  151. package/build/src/duration.js +5 -1
  152. package/build/src/git/admission.js +5 -3
  153. package/build/src/git/hooks.d.ts +8 -0
  154. package/build/src/git/hooks.js +63 -14
  155. package/build/src/git/identity.js +1 -4
  156. package/build/src/git/integration.d.ts +2 -3
  157. package/build/src/git/integration.js +32 -39
  158. package/build/src/git/nuke.d.ts +1 -1
  159. package/build/src/git/nuke.js +110 -57
  160. package/build/src/git/observe.d.ts +13 -1
  161. package/build/src/git/observe.js +51 -44
  162. package/build/src/git/process.js +4 -2
  163. package/build/src/git/read-observation.js +10 -8
  164. package/build/src/git/reconcile.d.ts +22 -2
  165. package/build/src/git/reconcile.js +38 -274
  166. package/build/src/git/repository.d.ts +2 -0
  167. package/build/src/git/repository.js +13 -22
  168. package/build/src/git/scratch.d.ts +8 -5
  169. package/build/src/git/scratch.js +29 -2
  170. package/build/src/git/target-placement.d.ts +9 -10
  171. package/build/src/git/target-placement.js +59 -62
  172. package/build/src/git/tender.js +13 -46
  173. package/build/src/git/terminal-reconcile.d.ts +21 -0
  174. package/build/src/git/terminal-reconcile.js +278 -0
  175. package/build/src/git/terminal-seal.js +15 -12
  176. package/build/src/git/tree.js +5 -2
  177. package/build/src/git/workspace.d.ts +24 -2
  178. package/build/src/git/workspace.js +120 -16
  179. package/build/src/identity/coordinates.js +3 -1
  180. package/build/src/identity/selector.js +6 -3
  181. package/build/src/kanshi/fleet.d.ts +6 -0
  182. package/build/src/kanshi/fleet.js +27 -0
  183. package/build/src/kanshi/index.d.ts +1 -1
  184. package/build/src/kanshi/read.js +60 -48
  185. package/build/src/kanshi/report.d.ts +11 -23
  186. package/build/src/kanshi/select.js +25 -24
  187. package/build/src/library/address.d.ts +2 -1
  188. package/build/src/library/address.js +7 -8
  189. package/build/src/library/akuma-creation.js +20 -12
  190. package/build/src/library/audit.d.ts +0 -1
  191. package/build/src/library/audit.js +1 -2
  192. package/build/src/library/bind.d.ts +53 -5
  193. package/build/src/library/bind.js +98 -22
  194. package/build/src/library/catalog.d.ts +1 -0
  195. package/build/src/library/catalog.js +6 -3
  196. package/build/src/library/configuration.js +3 -1
  197. package/build/src/library/continuation.d.ts +15 -4
  198. package/build/src/library/continuation.js +24 -16
  199. package/build/src/library/contract-bind.d.ts +26 -0
  200. package/build/src/library/contract-bind.js +106 -0
  201. package/build/src/library/contract-operations.d.ts +77 -0
  202. package/build/src/library/contract-operations.js +130 -0
  203. package/build/src/library/contract-types.d.ts +95 -0
  204. package/build/src/library/contract-types.js +1 -0
  205. package/build/src/library/contract.d.ts +19 -154
  206. package/build/src/library/contract.js +46 -261
  207. package/build/src/library/fleet.d.ts +1 -1
  208. package/build/src/library/fleet.js +19 -11
  209. package/build/src/library/input.d.ts +2 -0
  210. package/build/src/library/input.js +14 -0
  211. package/build/src/library/keiyaku.d.ts +8 -7
  212. package/build/src/library/keiyaku.js +3 -3
  213. package/build/src/library/nuke.js +14 -5
  214. package/build/src/library/reconcile.d.ts +9 -4
  215. package/build/src/library/reconcile.js +30 -24
  216. package/build/src/library/refusal.d.ts +13 -5
  217. package/build/src/library/refusal.js +7 -0
  218. package/build/src/library/region.d.ts +8 -7
  219. package/build/src/library/region.js +31 -11
  220. package/build/src/library/repo.js +2 -2
  221. package/build/src/markdown/lex.js +9 -2
  222. package/build/src/markdown/parse.js +11 -7
  223. package/build/src/markdown/query.js +4 -2
  224. package/build/src/protocol/amend.js +3 -1
  225. package/build/src/protocol/attempt.d.ts +7 -3
  226. package/build/src/protocol/attempt.js +9 -5
  227. package/build/src/protocol/audit.d.ts +1 -1
  228. package/build/src/protocol/audit.js +4 -14
  229. package/build/src/protocol/bind.d.ts +19 -6
  230. package/build/src/protocol/bind.js +28 -13
  231. package/build/src/protocol/completion.d.ts +0 -1
  232. package/build/src/protocol/completion.js +1 -3
  233. package/build/src/protocol/deliver.d.ts +1 -3
  234. package/build/src/protocol/deliver.js +15 -39
  235. package/build/src/protocol/intent.d.ts +1 -0
  236. package/build/src/protocol/intent.js +9 -7
  237. package/build/src/protocol/operations.d.ts +3 -7
  238. package/build/src/protocol/operations.js +5 -3
  239. package/build/src/protocol/placement.d.ts +0 -1
  240. package/build/src/protocol/placement.js +6 -6
  241. package/build/src/protocol/read/observation.d.ts +11 -0
  242. package/build/src/protocol/read/observation.js +37 -0
  243. package/build/src/protocol/read/status.d.ts +23 -12
  244. package/build/src/protocol/read/status.js +90 -72
  245. package/build/src/protocol/reconcile.d.ts +1 -0
  246. package/build/src/protocol/reconcile.js +2 -3
  247. package/build/src/protocol/reintegrate.js +1 -1
  248. package/build/src/protocol/review.d.ts +0 -1
  249. package/build/src/protocol/review.js +15 -22
  250. package/build/src/protocol/run.d.ts +1 -0
  251. package/build/src/protocol/run.js +4 -5
  252. package/build/src/runtime/proc/launch.d.ts +33 -0
  253. package/build/src/runtime/proc/launch.js +98 -0
  254. package/build/src/runtime/proc/line-rpc.js +9 -3
  255. package/build/src/runtime/proc/run.d.ts +12 -6
  256. package/build/src/runtime/proc/run.js +78 -21
  257. package/build/src/runtime/proc/stdio.js +7 -10
  258. package/build/src/settings.js +6 -2
  259. package/build/src/settlement/holder.js +13 -10
  260. package/build/src/settlement/settle.js +39 -6
  261. package/build/src/task/board.js +12 -6
  262. package/build/src/task/compose.js +85 -22
  263. package/build/src/task/context.js +21 -8
  264. package/build/src/task/document.js +46 -10
  265. package/build/src/task/identity.js +5 -2
  266. package/build/src/task/index.d.ts +0 -1
  267. package/build/src/task/index.js +119 -34
  268. package/build/src/task/input.js +29 -7
  269. package/build/src/task/mutation.js +61 -19
  270. package/build/src/task/operations.d.ts +6 -1
  271. package/build/src/task/operations.js +83 -29
  272. package/build/src/task/query.js +55 -26
  273. package/build/src/task/store.d.ts +3 -1
  274. package/build/src/task/store.js +81 -17
  275. package/build/src/verification/execution.js +14 -11
  276. package/build/src/workspace-place.js +19 -7
  277. package/build/src/world.js +16 -6
  278. package/package.json +8 -3
@@ -41,8 +41,9 @@ function decode(path, bytes) {
41
41
  if (keys.length !== 3 || keys.some((key) => !["akuId", "contractId", "dispatchedAt"].includes(key))) {
42
42
  corruption(`Dispatch has invalid fields: ${path}`);
43
43
  }
44
- if (typeof record.akuId !== "string" || typeof record.contractId !== "string"
45
- || typeof record.dispatchedAt !== "string") {
44
+ if (typeof record.akuId !== "string" ||
45
+ typeof record.contractId !== "string" ||
46
+ typeof record.dispatchedAt !== "string") {
46
47
  corruption(`Dispatch has invalid values: ${path}`);
47
48
  }
48
49
  let akuId;
@@ -81,8 +82,7 @@ export async function readDispatch(repository, value) {
81
82
  return await dispatchFromSnapshot(repository, await readGitPaths(repository, [path]), akuId);
82
83
  }
83
84
  export async function readDispatchesAt(observation) {
84
- const entries = [...observation.snapshot.paths]
85
- .filter(([path, entry]) => path.startsWith(DISPATCH_PREFIX) && entry.type === "blob");
85
+ const entries = [...observation.snapshot.paths].filter(([path, entry]) => path.startsWith(DISPATCH_PREFIX) && entry.type === "blob");
86
86
  const blobs = await observation.readBlobs(entries.map(([, entry]) => entry.oid));
87
87
  const dispatches = [];
88
88
  const seen = new Set();
@@ -151,11 +151,13 @@ export async function publishDispatch(input) {
151
151
  message: `dispatch ${akuId}`,
152
152
  at: intended.dispatchedAt,
153
153
  });
154
- const publication = await updateRefsAtomically(input.repository, [{
154
+ const publication = await updateRefsAtomically(input.repository, [
155
+ {
155
156
  ref: GIT_REF,
156
157
  newOid: commit,
157
158
  expectedOid: snapshot.commit,
158
- }]);
159
+ },
160
+ ]);
159
161
  if (publication.kind === "published")
160
162
  return { kind: "dispatched", dispatch: intended };
161
163
  const observed = await observedPublication(input.repository, akuId, owner);
@@ -14,7 +14,11 @@ export function parseDuration(value) {
14
14
  return { kind: "parsed", milliseconds: Number(milliseconds) };
15
15
  }
16
16
  export function formatDuration(milliseconds) {
17
- for (const [unit, multiplier] of [["h", 3_600_000], ["m", 60_000], ["s", 1_000]]) {
17
+ for (const [unit, multiplier] of [
18
+ ["h", 3_600_000],
19
+ ["m", 60_000],
20
+ ["s", 1_000],
21
+ ]) {
18
22
  if (milliseconds >= multiplier && milliseconds % multiplier === 0)
19
23
  return `${milliseconds / multiplier}${unit}`;
20
24
  }
@@ -1,4 +1,4 @@
1
- import { decodeJournal, encodeEntry, } from "../core/facts/codec.js";
1
+ import { decodeJournal, encodeEntry } from "../core/facts/codec.js";
2
2
  import { GIT_FORMAT_BYTES, GIT_FORMAT_PATH, GIT_REF, updateGitTreeFromFrozenDirectories, updateRefsAtomically, writeBlob, writeCommit, } from "./repository.js";
3
3
  import { contractJournalPath, contractJournalPaths, gitObjectIdForSnapshot, mintContractHead, mintSnapshotId, } from "./identity.js";
4
4
  import { validPath } from "./tree.js";
@@ -95,11 +95,13 @@ async function publishOffer(repository, admission, target, changes, assertions)
95
95
  { ref: GIT_REF, newOid: gitObjectIdForSnapshot(gitCommit), expectedOid: snapshot.commit },
96
96
  ...(target === null
97
97
  ? []
98
- : [{
98
+ : [
99
+ {
99
100
  ref: target.target,
100
101
  newOid: gitObjectIdForSnapshot(target.newOid),
101
102
  expectedOid: gitObjectIdForSnapshot(target.expectedOid),
102
- }]),
103
+ },
104
+ ]),
103
105
  ], assertions);
104
106
  }
105
107
  export async function admit(repository, offer, admission, assertions = []) {
@@ -29,6 +29,14 @@ export type WorktreeHookLag = Readonly<{
29
29
  }>;
30
30
  type HookPhase = "create" | "destroy";
31
31
  export declare function hookMarkerPath(administrationDirectory: string): string;
32
+ export type WorktreeHookMarkerObservation = Readonly<{
33
+ kind: "absent" | "pending" | "ok";
34
+ }> | Readonly<{
35
+ kind: "failed";
36
+ diagnostic: string;
37
+ }>;
38
+ /** Read the durable hook marker without executing commands or taking locks. */
39
+ export declare function observeWorktreeHookMarker(administrationDirectory: string): Promise<WorktreeHookMarkerObservation>;
32
40
  export type HookCommandRun = Readonly<{
33
41
  kind: "ok";
34
42
  }> | Readonly<{
@@ -24,7 +24,9 @@ function decodeCommand(value, coordinate) {
24
24
  }
25
25
  if (value.argv[0].trim().length === 0)
26
26
  throw new Error(`${coordinate}.argv[0] must be nonblank`);
27
- if (!Number.isSafeInteger(value.timeoutMs) || value.timeoutMs < 1 || value.timeoutMs > 2_147_483_647) {
27
+ if (!Number.isSafeInteger(value.timeoutMs) ||
28
+ value.timeoutMs < 1 ||
29
+ value.timeoutMs > 2_147_483_647) {
28
30
  throw new Error(`${coordinate}.timeoutMs is invalid`);
29
31
  }
30
32
  return { argv: value.argv, timeoutMs: value.timeoutMs };
@@ -40,10 +42,19 @@ function decodeFailure(value, coordinate) {
40
42
  switch (value.kind) {
41
43
  case "exit":
42
44
  exactKeys(value, ["kind", "code", "stdout", "stderr", "truncated"], coordinate);
43
- if (!Number.isInteger(value.code) || typeof value.stdout !== "string" || typeof value.stderr !== "string" || typeof value.truncated !== "boolean") {
45
+ if (!Number.isInteger(value.code) ||
46
+ typeof value.stdout !== "string" ||
47
+ typeof value.stderr !== "string" ||
48
+ typeof value.truncated !== "boolean") {
44
49
  throw new Error(`${coordinate} is invalid`);
45
50
  }
46
- return { kind: "exit", code: value.code, stdout: value.stdout, stderr: value.stderr, truncated: value.truncated };
51
+ return {
52
+ kind: "exit",
53
+ code: value.code,
54
+ stdout: value.stdout,
55
+ stderr: value.stderr,
56
+ truncated: value.truncated,
57
+ };
47
58
  case "timeout":
48
59
  case "unknown-exit":
49
60
  exactKeys(value, ["kind"], coordinate);
@@ -76,7 +87,11 @@ function decodeProgress(value, commands, coordinate) {
76
87
  if (!Number.isInteger(value.command) || value.command < 0 || value.command >= commands) {
77
88
  throw new Error(`${coordinate}.command is invalid`);
78
89
  }
79
- return { status: "failed", command: value.command, failure: decodeFailure(value.failure, `${coordinate}.failure`) };
90
+ return {
91
+ status: "failed",
92
+ command: value.command,
93
+ failure: decodeFailure(value.failure, `${coordinate}.failure`),
94
+ };
80
95
  }
81
96
  throw new Error(`${coordinate}.status is invalid`);
82
97
  }
@@ -114,6 +129,28 @@ async function readMarker(administrationDirectory) {
114
129
  throw error;
115
130
  }
116
131
  }
132
+ function hookFailureDiagnostic(phase, progress) {
133
+ const failure = progress.failure;
134
+ const detail = failure.kind === "exit"
135
+ ? `exit=${failure.code}`
136
+ : failure.kind === "spawn-error"
137
+ ? failure.diagnostic
138
+ : failure.kind;
139
+ return `worktree-hook-failed ${phase} command=${progress.command} ${detail}`;
140
+ }
141
+ /** Read the durable hook marker without executing commands or taking locks. */
142
+ export async function observeWorktreeHookMarker(administrationDirectory) {
143
+ const marker = await readMarker(administrationDirectory);
144
+ if (marker === null)
145
+ return { kind: "absent" };
146
+ if (marker.create.status === "failed")
147
+ return { kind: "failed", diagnostic: hookFailureDiagnostic("create", marker.create) };
148
+ if (marker.destroy.status === "failed")
149
+ return { kind: "failed", diagnostic: hookFailureDiagnostic("destroy", marker.destroy) };
150
+ if (marker.create.status === "pending" || marker.destroy.status === "pending")
151
+ return { kind: "pending" };
152
+ return { kind: "ok" };
153
+ }
117
154
  async function writeMarker(administrationDirectory, marker) {
118
155
  const path = hookMarkerPath(administrationDirectory);
119
156
  await mkdir(dirname(path), { recursive: true });
@@ -129,13 +166,15 @@ function freshMarker(commands, createPending) {
129
166
  }
130
167
  function failedOutcome(outcome) {
131
168
  if (outcome.kind === "terminal") {
132
- return outcome.code === 0 ? null : {
133
- kind: "exit",
134
- code: outcome.code,
135
- stdout: outcome.stdout,
136
- stderr: outcome.stderr,
137
- truncated: outcome.truncated,
138
- };
169
+ return outcome.code === 0
170
+ ? null
171
+ : {
172
+ kind: "exit",
173
+ code: outcome.code,
174
+ stdout: outcome.stdout,
175
+ stderr: outcome.stderr,
176
+ truncated: outcome.truncated,
177
+ };
139
178
  }
140
179
  return outcome;
141
180
  }
@@ -239,7 +278,9 @@ async function executePendingCommand(input) {
239
278
  const failure = result.kind === "failed" ? result.failure : null;
240
279
  const next = input.command + 1;
241
280
  await writeMarker(input.administrationDirectory, withProgress(marker, input.phase, failure === null
242
- ? next === marker.commands[input.phase].length ? { status: "ok" } : { status: "pending", next }
281
+ ? next === marker.commands[input.phase].length
282
+ ? { status: "ok" }
283
+ : { status: "pending", next }
243
284
  : { status: "failed", command: input.command, failure }));
244
285
  }
245
286
  finally {
@@ -280,7 +321,13 @@ async function runPhase(worktree, administrationDirectory, hooks, phase, retryHo
280
321
  return null;
281
322
  if (progress.status === "failed") {
282
323
  if (!retryFailure) {
283
- return { kind: "worktree-hook-failed", phase, path: worktree, command: progress.command, failure: progress.failure };
324
+ return {
325
+ kind: "worktree-hook-failed",
326
+ phase,
327
+ path: worktree,
328
+ command: progress.command,
329
+ failure: progress.failure,
330
+ };
284
331
  }
285
332
  retryFailure = false;
286
333
  await writeMarker(administrationDirectory, withProgress(marker, phase, { status: "pending", next: progress.command }));
@@ -299,7 +346,9 @@ export function runCreateHooks(worktree, administrationDirectory, hooks, retryHo
299
346
  export function runDestroyHooks(worktree, administrationDirectory, hooks, retryHooks) {
300
347
  return runPhase(worktree, administrationDirectory, hooks, "destroy", retryHooks);
301
348
  }
302
- if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1] && process.argv[2] === "--run-hook") {
349
+ if (process.argv[1] !== undefined &&
350
+ fileURLToPath(import.meta.url) === process.argv[1] &&
351
+ process.argv[2] === "--run-hook") {
303
352
  const encoded = process.argv[3];
304
353
  if (encoded === undefined)
305
354
  throw new Error("Hook runner input is missing");
@@ -23,10 +23,7 @@ export function contractJournalPath(contract, journalClass = "active") {
23
23
  return `contracts/${journalClass}/${contractJournalSuffix(contract)}`;
24
24
  }
25
25
  export function contractJournalPaths(contract) {
26
- return [
27
- contractJournalPath(contract, "active"),
28
- contractJournalPath(contract, "terminal"),
29
- ];
26
+ return [contractJournalPath(contract, "active"), contractJournalPath(contract, "terminal")];
30
27
  }
31
28
  export function gitObjectId(value, label = "Git object ID") {
32
29
  if (!GIT_OBJECT_ID.test(value))
@@ -3,6 +3,8 @@ import type { ChangeId, ContractCoordinates, ContractId, SnapshotId } from "../c
3
3
  import { type GitObjectId } from "./identity.js";
4
4
  import { type GitRepository } from "./process.js";
5
5
  import type { DeliveryCommitMetadata, TenderCapture } from "./tender.js";
6
+ import { workspaceMergeStatePresent } from "./workspace.js";
7
+ export { workspaceMergeStatePresent };
6
8
  type IntegrationTender = Pick<TenderCapture, "head" | "tree">;
7
9
  declare const REQUIRED_GIT: "2.38";
8
10
  export type IntegrationPreparationRefusal = Readonly<{
@@ -30,8 +32,6 @@ export declare function worktreeChangeId(repository: GitRepository, input: Integ
30
32
  export declare function materializeIntegrationSnapshot(repository: GitRepository, tree: GitObjectId, parent: SnapshotId, metadata: DeliveryCommitMetadata): Promise<SnapshotId>;
31
33
  export declare function persistedTender(repository: GitRepository, snapshot: SnapshotId): Promise<IntegrationTender>;
32
34
  export declare function materializeReintegrationSnapshot(repository: GitRepository, tree: GitObjectId, parent: SnapshotId, template: SnapshotId): Promise<SnapshotId>;
33
- /** True when the appointed workspace already has Git MERGE_HEAD. */
34
- export declare function workspaceMergeStatePresent(repository: GitRepository, workspace: string): Promise<boolean>;
35
35
  /** Project the already-judged conflict as `git merge --no-commit <targetHead>`. */
36
36
  export declare function materializeJudgedConflict(repository: GitRepository, workspace: string, targetHead: SnapshotId): Promise<void>;
37
37
  export declare function planIntegration(repository: GitRepository, input: IntegrationCoordinates, tender: IntegrationTender, requireBranchesToBeUpToDate: boolean): Promise<Preparation<IntegrationPlan, Readonly<{
@@ -48,4 +48,3 @@ export type DeliveryDiffScope = Readonly<{
48
48
  export declare function readDeliveryScope(repository: GitRepository, predecessor: SnapshotId, candidate: SnapshotId, includePaths: boolean): Promise<DeliveryDiffScope>;
49
49
  /** Read a recorded integration pair's patch, or null when Git no longer has it. */
50
50
  export declare function readDeliveryDiff(repository: GitRepository, predecessor: SnapshotId, candidate: SnapshotId): Promise<string | null>;
51
- export {};
@@ -1,7 +1,9 @@
1
1
  import { AuthorityCorruptionError } from "../core/facts/errors.js";
2
2
  import { gitObjectId, gitObjectIdForSnapshot, mintChangeId, mintSnapshotId } from "./identity.js";
3
- import { decodeGitNameOnly, decodeGitNumstat, readRef, } from "./repository.js";
4
- import { GitPlumbingError, runGit, runGitWithEnvironment, } from "./process.js";
3
+ import { decodeGitNameOnly, decodeGitNumstat, readRef } from "./repository.js";
4
+ import { GitPlumbingError, runGit, runGitWithEnvironment } from "./process.js";
5
+ import { workspaceMergeStatePresent } from "./workspace.js";
6
+ export { workspaceMergeStatePresent };
5
7
  const REQUIRED_GIT = "2.38";
6
8
  async function runAllowingNonzero(repository, args, input) {
7
9
  try {
@@ -16,12 +18,18 @@ async function runAllowingNonzero(repository, args, input) {
16
18
  /** Mint the one worktree-content identity shared by review and delivery. */
17
19
  export async function worktreeChangeId(repository, input, tender) {
18
20
  const patch = await runGit(repository, [
19
- "-c", "core.quotePath=false",
20
- "-c", "core.abbrev=40",
21
- "-c", "diff.algorithm=myers",
22
- "-c", "diff.renames=false",
23
- "-c", "diff.indentHeuristic=false",
24
- "-c", "diff.suppressBlankEmpty=false",
21
+ "-c",
22
+ "core.quotePath=false",
23
+ "-c",
24
+ "core.abbrev=40",
25
+ "-c",
26
+ "diff.algorithm=myers",
27
+ "-c",
28
+ "diff.renames=false",
29
+ "-c",
30
+ "diff.indentHeuristic=false",
31
+ "-c",
32
+ "diff.suppressBlankEmpty=false",
25
33
  "diff",
26
34
  "--no-ext-diff",
27
35
  "--no-textconv",
@@ -53,7 +61,9 @@ export async function materializeIntegrationSnapshot(repository, tree, parent, m
53
61
  GIT_COMMITTER_EMAIL: metadata.identity.email,
54
62
  GIT_AUTHOR_DATE: metadata.at,
55
63
  GIT_COMMITTER_DATE: metadata.at,
56
- })).toString("utf8").trim();
64
+ }))
65
+ .toString("utf8")
66
+ .trim();
57
67
  return mintSnapshotId(commit);
58
68
  }
59
69
  export async function persistedTender(repository, snapshot) {
@@ -80,12 +90,9 @@ export async function materializeReintegrationSnapshot(repository, tree, parent,
80
90
  const message = raw.subarray(separator + 2);
81
91
  let fields;
82
92
  try {
83
- const formatted = (await runGit(repository, [
84
- "show",
85
- "-s",
86
- "--format=%an%x00%ae%x00%aI%x00%cn%x00%ce%x00%cI",
87
- templateObject,
88
- ])).toString("utf8").replace(/\n$/u, "");
93
+ const formatted = (await runGit(repository, ["show", "-s", "--format=%an%x00%ae%x00%aI%x00%cn%x00%ce%x00%cI", templateObject]))
94
+ .toString("utf8")
95
+ .replace(/\n$/u, "");
89
96
  fields = formatted.split("\0");
90
97
  }
91
98
  catch (error) {
@@ -105,7 +112,9 @@ export async function materializeReintegrationSnapshot(repository, tree, parent,
105
112
  GIT_COMMITTER_NAME: committerName,
106
113
  GIT_COMMITTER_EMAIL: committerEmail,
107
114
  GIT_COMMITTER_DATE: committerDate,
108
- })).toString("utf8").trim();
115
+ }))
116
+ .toString("utf8")
117
+ .trim();
109
118
  return mintSnapshotId(commit);
110
119
  }
111
120
  async function isAncestor(repository, ancestor, descendant) {
@@ -192,26 +201,6 @@ async function mergedTree(repository, contractId, base, targetHead, tenderTree)
192
201
  message: "git merge-tree --write-tree failed",
193
202
  });
194
203
  }
195
- /** True when the appointed workspace already has Git MERGE_HEAD. */
196
- export async function workspaceMergeStatePresent(repository, workspace) {
197
- const result = await runAllowingNonzero(repository, [
198
- "-C",
199
- workspace,
200
- "rev-parse",
201
- "-q",
202
- "--verify",
203
- "MERGE_HEAD",
204
- ]);
205
- if (result.status === 0)
206
- return true;
207
- if (result.status === 1)
208
- return false;
209
- throw new GitPlumbingError({
210
- stderr: result.stderr,
211
- status: result.status,
212
- message: "git rev-parse --verify MERGE_HEAD failed",
213
- });
214
- }
215
204
  /** Project the already-judged conflict as `git merge --no-commit <targetHead>`. */
216
205
  export async function materializeJudgedConflict(repository, workspace, targetHead) {
217
206
  const result = await runAllowingNonzero(repository, [
@@ -289,7 +278,11 @@ export async function planIntegration(repository, input, tender, requireBranches
289
278
  }
290
279
  async function deliverySnapshotAvailability(repository, predecessor, candidate) {
291
280
  const objects = [gitObjectIdForSnapshot(predecessor), gitObjectIdForSnapshot(candidate)];
292
- const types = (await runGit(repository, ["cat-file", "--batch-check=%(objectname) %(objecttype)"], `${objects.join("\n")}\n`)).toString("ascii").trimEnd().split("\n").map((record) => record.split(" ")[1]);
281
+ const types = (await runGit(repository, ["cat-file", "--batch-check=%(objectname) %(objecttype)"], `${objects.join("\n")}\n`))
282
+ .toString("ascii")
283
+ .trimEnd()
284
+ .split("\n")
285
+ .map((record) => record.split(" ")[1]);
293
286
  if (types.includes("missing"))
294
287
  return "unavailable";
295
288
  if (types.some((type) => type !== "commit")) {
@@ -322,7 +315,7 @@ export async function readDeliveryScope(repository, predecessor, candidate, incl
322
315
  }
323
316
  /** Read a recorded integration pair's patch, or null when Git no longer has it. */
324
317
  export async function readDeliveryDiff(repository, predecessor, candidate) {
325
- if (await deliverySnapshotAvailability(repository, predecessor, candidate) === "unavailable")
318
+ if ((await deliverySnapshotAvailability(repository, predecessor, candidate)) === "unavailable")
326
319
  return null;
327
320
  try {
328
321
  return (await runGit(repository, [
@@ -334,7 +327,7 @@ export async function readDeliveryDiff(repository, predecessor, candidate) {
334
327
  ])).toString("utf8");
335
328
  }
336
329
  catch (error) {
337
- if (await deliverySnapshotAvailability(repository, predecessor, candidate) === "unavailable")
330
+ if ((await deliverySnapshotAvailability(repository, predecessor, candidate)) === "unavailable")
338
331
  return null;
339
332
  throw error;
340
333
  }
@@ -1,2 +1,2 @@
1
1
  import type { WorldRoot } from "../world.js";
2
- export declare function nukeGit(world: WorldRoot): Promise<void>;
2
+ export declare function nukeGit(world: WorldRoot, gitPath?: string): Promise<void>;
@@ -1,52 +1,90 @@
1
- import { access } from "node:fs/promises";
2
- import { nukeHereAppointments } from "../contract-worktree.js";
3
- import { nukeEmptyPlaceAuthority, readPlaceRegister, releaseManagedWorktrees, } from "../workspace-place.js";
1
+ import { access, lstat, realpath, rm } from "node:fs/promises";
2
+ import { isAbsolute, relative, sep } from "node:path";
3
+ import { nukeEmptyPlaceAuthority, readPlaceRegister, releaseManagedWorktrees } from "../workspace-place.js";
4
4
  import { nukeWorktreeHookResidue } from "./hooks.js";
5
5
  import { runGit } from "./process.js";
6
- import { NoGitWorldError, registeredWorktrees, repositoryAt, worktreeGitDirectory } from "./repository.js";
6
+ import { CANDIDATE_PIN_REF_NAMESPACE, DELIVERY_REF_NAMESPACE, GIT_REF, MIGRATION_CANDIDATE_PIN_REF_NAMESPACE, MIGRATION_DELIVERY_REF_NAMESPACE, NoGitWorldError, readRef, registeredWorktrees, repositoryAt, worktreeGitDirectory, } from "./repository.js";
7
7
  import { worktreePath } from "./workspace.js";
8
8
  async function managedCustody(repository) {
9
9
  const register = await readPlaceRegister(repository);
10
- const topology = new Map((await registeredWorktrees(repository)).map((entry) => [entry.path, entry]));
11
- const entries = [];
12
- for (const appointment of register.appointments) {
13
- const path = worktreePath(repository, appointment.place);
14
- const registered = topology.get(path);
15
- if (registered === undefined) {
16
- try {
17
- await access(path);
18
- }
19
- catch {
20
- entries.push({ contract: appointment.contract, path });
21
- continue;
22
- }
23
- throw new Error(`managed Place path has foreign custody: ${path}`);
24
- }
25
- if (registered.branch !== null)
26
- throw new Error(`managed Place path has a branch: ${path}`);
27
- entries.push({ contract: appointment.contract, path });
28
- }
29
- const refs = (await runGit(repository, [
30
- "for-each-ref", "--format=%(refname)", "refs/heads/keiyaku-delivery", "refs/heads/keiyaku-candidate",
31
- ])).toString("utf8").split("\n").filter((ref) => ref.length > 0);
32
- const state = await runGit(repository, ["show-ref", "--verify", "--quiet", "refs/heads/keiyaku-state"])
33
- .then(() => true, () => false);
34
- return { entries, refs, state };
10
+ return {
11
+ entries: register.appointments.map((appointment) => ({
12
+ contract: appointment.contract,
13
+ path: worktreePath(repository, appointment.place),
14
+ })),
15
+ };
35
16
  }
36
- async function removeManagedWorktree(repository, entry) {
37
- const topology = await registeredWorktrees(repository);
38
- const registered = topology.find((candidate) => candidate.path === entry.path);
39
- if (registered === undefined) {
40
- try {
41
- await access(entry.path);
42
- }
43
- catch {
44
- return;
45
- }
17
+ function underCommonDirectory(commonDirectory, administrationDirectory) {
18
+ const suffix = relative(commonDirectory, administrationDirectory);
19
+ return suffix.length > 0 && suffix !== ".." && !suffix.startsWith(`..${sep}`) && !isAbsolute(suffix);
20
+ }
21
+ async function unregisteredResidueBelongsToRepository(repository, path) {
22
+ let physical;
23
+ try {
24
+ physical = await lstat(path);
25
+ }
26
+ catch (error) {
27
+ if (error.code === "ENOENT")
28
+ return true;
29
+ throw error;
30
+ }
31
+ if (!physical.isDirectory() || physical.isSymbolicLink())
32
+ return false;
33
+ let administrationDirectory;
34
+ try {
35
+ administrationDirectory = await worktreeGitDirectory(repository, path);
36
+ }
37
+ catch {
38
+ return false;
39
+ }
40
+ try {
41
+ const [common, administration] = await Promise.all([
42
+ realpath(repository.commonDirectory),
43
+ realpath(administrationDirectory),
44
+ ]);
45
+ return underCommonDirectory(common, administration);
46
+ }
47
+ catch (error) {
48
+ if (error.code === "ENOENT")
49
+ return false;
50
+ throw error;
51
+ }
52
+ }
53
+ async function removeUnregisteredResidue(repository, entry) {
54
+ let present = true;
55
+ try {
56
+ await access(entry.path);
57
+ }
58
+ catch (error) {
59
+ if (error.code === "ENOENT")
60
+ present = false;
61
+ else
62
+ throw error;
63
+ }
64
+ if (!present)
65
+ return true;
66
+ if (!(await unregisteredResidueBelongsToRepository(repository, entry.path))) {
46
67
  throw new Error(`managed Place path has foreign custody: ${entry.path}`);
47
68
  }
69
+ const administrationDirectory = await worktreeGitDirectory(repository, entry.path);
70
+ await nukeWorktreeHookResidue(administrationDirectory);
71
+ await rm(entry.path, { recursive: true, force: true });
72
+ try {
73
+ await access(entry.path);
74
+ }
75
+ catch (error) {
76
+ if (error.code === "ENOENT")
77
+ return true;
78
+ throw error;
79
+ }
80
+ throw new Error(`managed Place worktree remains present: ${entry.path}`);
81
+ }
82
+ async function removeManagedWorktree(repository, entry) {
83
+ const registered = (await registeredWorktrees(repository)).find((candidate) => candidate.path === entry.path);
84
+ if (registered === undefined)
85
+ return await removeUnregisteredResidue(repository, entry);
48
86
  if (registered.branch !== null)
49
- throw new Error(`managed Place path has a branch: ${entry.path}`);
87
+ return false;
50
88
  await nukeWorktreeHookResidue(await worktreeGitDirectory(repository, entry.path));
51
89
  await runGit(repository, ["worktree", "remove", "--force", entry.path]);
52
90
  if ((await registeredWorktrees(repository)).some((candidate) => candidate.path === entry.path)) {
@@ -55,38 +93,53 @@ async function removeManagedWorktree(repository, entry) {
55
93
  try {
56
94
  await access(entry.path);
57
95
  }
58
- catch {
59
- return;
96
+ catch (error) {
97
+ if (error.code === "ENOENT")
98
+ return true;
99
+ throw error;
60
100
  }
61
101
  throw new Error(`managed Place worktree remains present: ${entry.path}`);
62
102
  }
63
- async function removeOwnedRefs(repository, refs) {
64
- for (const ref of refs) {
65
- const oid = (await runGit(repository, ["rev-parse", "--verify", "--quiet", ref])).toString("utf8").trim();
66
- if (oid.length > 0)
67
- await runGit(repository, ["update-ref", "--no-deref", "-d", ref, oid]);
103
+ async function deleteRefAt(repository, ref, expectedOid) {
104
+ await runGit(repository, ["update-ref", "--no-deref", "-d", ref, expectedOid]);
105
+ }
106
+ async function removeOwnedRefs(repository) {
107
+ const roots = [
108
+ DELIVERY_REF_NAMESPACE,
109
+ CANDIDATE_PIN_REF_NAMESPACE,
110
+ MIGRATION_DELIVERY_REF_NAMESPACE,
111
+ MIGRATION_CANDIDATE_PIN_REF_NAMESPACE,
112
+ ];
113
+ for (const root of roots) {
114
+ const refs = (await runGit(repository, ["for-each-ref", "--format=%(refname)", root]))
115
+ .toString("utf8")
116
+ .split("\n")
117
+ .filter((ref) => ref.length > root.length && ref.startsWith(`${root}/`));
118
+ for (const ref of refs) {
119
+ const oid = await readRef(repository, ref);
120
+ if (oid !== null)
121
+ await deleteRefAt(repository, ref, oid);
122
+ }
68
123
  }
69
124
  }
70
- export async function nukeGit(world) {
125
+ export async function nukeGit(world, gitPath = "git") {
71
126
  let repository;
72
127
  try {
73
- repository = await repositoryAt(world);
128
+ repository = await repositoryAt(world, gitPath);
74
129
  }
75
130
  catch (error) {
76
131
  if (error instanceof NoGitWorldError)
77
132
  return;
78
133
  throw error;
79
134
  }
135
+ const state = await readRef(repository, GIT_REF);
136
+ if (state !== null)
137
+ await deleteRefAt(repository, GIT_REF, state);
80
138
  const custody = await managedCustody(repository);
81
139
  for (const entry of custody.entries) {
82
- await removeManagedWorktree(repository, entry);
83
- await releaseManagedWorktrees(repository, [entry.contract]);
140
+ if (await removeManagedWorktree(repository, entry))
141
+ await releaseManagedWorktrees(repository, [entry.contract]);
84
142
  }
85
- await nukeHereAppointments(repository);
86
143
  await nukeEmptyPlaceAuthority(repository);
87
- await removeOwnedRefs(repository, custody.refs);
88
- if (custody.state) {
89
- const oid = (await runGit(repository, ["rev-parse", "--verify", "refs/heads/keiyaku-state"])).toString("utf8").trim();
90
- await runGit(repository, ["update-ref", "--no-deref", "-d", "refs/heads/keiyaku-state", oid]);
91
- }
144
+ await removeOwnedRefs(repository);
92
145
  }
@@ -34,11 +34,23 @@ export type BindCoordinatesObservation = Readonly<{
34
34
  target?: string;
35
35
  branch: string | null;
36
36
  }>;
37
+ export type UnbornHeadObservation = Readonly<{
38
+ kind: "unborn-head";
39
+ branch: string | null;
40
+ }>;
41
+ export type BindTargetSelection = Readonly<{
42
+ kind: "explicit";
43
+ target: string;
44
+ }> | Readonly<{
45
+ kind: "current-branch";
46
+ }> | Readonly<{
47
+ kind: "targetless";
48
+ }>;
37
49
  export declare function normalizeTargetBranch(repository: GitRepository, input: string): Promise<string | null>;
38
50
  /** Read one worktree's attached branch, or null for detached HEAD. */
39
51
  export declare function currentBranch(repository: GitRepository, path?: string): Promise<string | null>;
40
52
  /** Observe bind's tender start and optional reward target without creating persistent state. */
41
- export declare function observeBindCoordinates(repository: GitRepository, requestedTarget?: string): Promise<BindCoordinatesObservation | null>;
53
+ export declare function observeBindCoordinates(repository: GitRepository, selection?: BindTargetSelection): Promise<BindCoordinatesObservation | UnbornHeadObservation | null>;
42
54
  export declare function observeActiveContractWorld(observation: GitReadObservation): Promise<ActiveContractWorldObservation>;
43
55
  export declare function observeContractWorld(observation: GitReadObservation, requested?: readonly ContractId[]): Promise<ContractWorldObservation>;
44
56
  export declare function observeContractsForAdmissionAt(repository: GitRepository, channel: GitDecodeChannel, ids: readonly ContractId[], selection?: GitTreeSelection): Promise<GitDecisionObservation>;