@astrosheep/keiyaku 4.0.0 → 4.0.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 (308) hide show
  1. package/README.md +133 -8
  2. package/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +2 -2
  3. package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +4 -4
  4. package/build/integrations/marketplace/plugins/keiyaku/opencode.js +2 -2
  5. package/build/integrations/marketplace/plugins/keiyaku/package.json +2 -2
  6. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +13 -5
  7. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +91 -32
  8. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +134 -0
  9. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/agents/openai.yaml +4 -0
  10. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +41 -6
  11. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +262 -46
  12. package/build/src/akuma/abort.d.ts +2 -0
  13. package/build/src/akuma/abort.js +50 -0
  14. package/build/src/akuma/akuma.d.ts +50 -33
  15. package/build/src/akuma/akuma.js +175 -127
  16. package/build/src/akuma/archetype.d.ts +14 -5
  17. package/build/src/akuma/archetype.js +85 -39
  18. package/build/src/akuma/body.d.ts +3 -5
  19. package/build/src/akuma/body.js +490 -183
  20. package/build/src/akuma/coordinate.d.ts +9 -0
  21. package/build/src/akuma/coordinate.js +22 -0
  22. package/build/src/akuma/heart/facts.d.ts +88 -45
  23. package/build/src/akuma/heart/facts.js +19 -8
  24. package/build/src/akuma/heart/index.d.ts +64 -55
  25. package/build/src/akuma/heart/index.js +172 -252
  26. package/build/src/akuma/heart/rows.d.ts +38 -83
  27. package/build/src/akuma/heart/rows.js +117 -155
  28. package/build/src/akuma/heart/schema.d.ts +1 -1
  29. package/build/src/akuma/heart/schema.js +61 -22
  30. package/build/src/akuma/heart/soul.d.ts +8 -0
  31. package/build/src/akuma/heart/soul.js +136 -0
  32. package/build/src/akuma/heart/storage.d.ts +47 -0
  33. package/build/src/akuma/heart/storage.js +224 -0
  34. package/build/src/akuma/heart/tells.d.ts +12 -0
  35. package/build/src/akuma/heart/tells.js +79 -0
  36. package/build/src/akuma/heart/timeline.d.ts +15 -0
  37. package/build/src/akuma/heart/timeline.js +86 -0
  38. package/build/src/akuma/identity.d.ts +2 -2
  39. package/build/src/akuma/identity.js +9 -9
  40. package/build/src/akuma/index.d.ts +10 -3
  41. package/build/src/akuma/index.js +7 -1
  42. package/build/src/akuma/projection.d.ts +176 -0
  43. package/build/src/akuma/projection.js +256 -0
  44. package/build/src/akuma/provider-recipe.d.ts +24 -0
  45. package/build/src/akuma/provider-recipe.js +100 -0
  46. package/build/src/akuma/provider.d.ts +67 -13
  47. package/build/src/akuma/provider.js +172 -105
  48. package/build/src/akuma/providers/acp/core.d.ts +19 -0
  49. package/build/src/akuma/providers/acp/core.js +159 -0
  50. package/build/src/akuma/providers/acp/events.d.ts +20 -0
  51. package/build/src/akuma/providers/acp/events.js +73 -0
  52. package/build/src/akuma/providers/acp/index.d.ts +12 -0
  53. package/build/src/akuma/providers/acp/index.js +94 -0
  54. package/build/src/akuma/providers/{claude.d.ts → claude/events.d.ts} +9 -41
  55. package/build/src/akuma/providers/claude/events.js +238 -0
  56. package/build/src/akuma/providers/claude/index.d.ts +47 -0
  57. package/build/src/akuma/providers/claude/index.js +279 -0
  58. package/build/src/akuma/providers/claude/input.d.ts +13 -0
  59. package/build/src/akuma/providers/claude/input.js +109 -0
  60. package/build/src/akuma/providers/codex-app-server/events.d.ts +1 -1
  61. package/build/src/akuma/providers/codex-app-server/events.js +15 -12
  62. package/build/src/akuma/providers/codex-app-server/index.d.ts +15 -13
  63. package/build/src/akuma/providers/codex-app-server/index.js +77 -33
  64. package/build/src/akuma/providers/grok-build/index.d.ts +4 -0
  65. package/build/src/akuma/providers/grok-build/index.js +70 -0
  66. package/build/src/akuma/providers/index.d.ts +5 -3
  67. package/build/src/akuma/providers/index.js +35 -56
  68. package/build/src/akuma/providers/opencode-sdk/events.d.ts +54 -0
  69. package/build/src/akuma/providers/opencode-sdk/events.js +235 -0
  70. package/build/src/akuma/providers/opencode-sdk/index.d.ts +33 -0
  71. package/build/src/akuma/providers/opencode-sdk/index.js +308 -0
  72. package/build/src/akuma/providers/opencode-sdk/session.d.ts +26 -0
  73. package/build/src/akuma/providers/opencode-sdk/session.js +63 -0
  74. package/build/src/akuma/providers/pi/events.d.ts +33 -0
  75. package/build/src/akuma/providers/pi/events.js +166 -0
  76. package/build/src/akuma/providers/pi/index.d.ts +11 -0
  77. package/build/src/akuma/providers/pi/index.js +191 -0
  78. package/build/src/akuma/publication.d.ts +3 -3
  79. package/build/src/akuma/publication.js +28 -40
  80. package/build/src/akuma/requests.d.ts +9 -6
  81. package/build/src/akuma/requests.js +95 -65
  82. package/build/src/alias/index.d.ts +4 -3
  83. package/build/src/alias/index.js +12 -13
  84. package/build/src/body/decode.js +7 -1
  85. package/build/src/body/region.d.ts +2 -0
  86. package/build/src/body/region.js +77 -7
  87. package/build/src/body/render.js +2 -1
  88. package/build/src/body/shape.js +7 -1
  89. package/build/src/body/verification.js +19 -4
  90. package/build/src/cli/accepted.d.ts +18 -12
  91. package/build/src/cli/accepted.js +73 -34
  92. package/build/src/cli/actor.js +3 -3
  93. package/build/src/cli/commands/akuma-invoke.d.ts +44 -19
  94. package/build/src/cli/commands/akuma-invoke.js +101 -52
  95. package/build/src/cli/commands/akuma.d.ts +27 -9
  96. package/build/src/cli/commands/akuma.js +151 -80
  97. package/build/src/cli/commands/contract.d.ts +155 -0
  98. package/build/src/cli/commands/contract.js +20 -0
  99. package/build/src/cli/commands/install.js +2 -0
  100. package/build/src/cli/commands/task-invoke.d.ts +19 -4
  101. package/build/src/cli/commands/task-invoke.js +65 -17
  102. package/build/src/cli/commands/task-query.d.ts +5 -0
  103. package/build/src/cli/commands/task-query.js +247 -0
  104. package/build/src/cli/commands/task.d.ts +3 -1
  105. package/build/src/cli/commands/task.js +52 -14
  106. package/build/src/cli/coordinates.d.ts +18 -0
  107. package/build/src/cli/coordinates.js +120 -0
  108. package/build/src/cli/draft.d.ts +8 -0
  109. package/build/src/cli/draft.js +96 -0
  110. package/build/src/cli/invoke.d.ts +9 -3
  111. package/build/src/cli/invoke.js +287 -108
  112. package/build/src/cli/main.js +54 -7
  113. package/build/src/cli/parse.d.ts +21 -117
  114. package/build/src/cli/parse.js +79 -84
  115. package/build/src/cli/render/akuma-tool-command.d.ts +6 -0
  116. package/build/src/cli/render/akuma-tool-command.js +144 -0
  117. package/build/src/cli/render/akuma-tool.d.ts +4 -2
  118. package/build/src/cli/render/akuma-tool.js +35 -5
  119. package/build/src/cli/render/akuma.d.ts +3 -1
  120. package/build/src/cli/render/akuma.js +247 -212
  121. package/build/src/cli/render/audit.d.ts +3 -0
  122. package/build/src/cli/render/audit.js +104 -0
  123. package/build/src/cli/render/catalog.d.ts +2 -0
  124. package/build/src/cli/render/catalog.js +16 -0
  125. package/build/src/cli/render/contract.d.ts +3 -3
  126. package/build/src/cli/render/contract.js +200 -54
  127. package/build/src/cli/render/kanshi.js +272 -129
  128. package/build/src/cli/render/receipt.d.ts +19 -0
  129. package/build/src/cli/render/receipt.js +106 -0
  130. package/build/src/cli/render/refusal.d.ts +16 -2
  131. package/build/src/cli/render/refusal.js +88 -3
  132. package/build/src/cli/render/region.d.ts +2 -0
  133. package/build/src/cli/render/region.js +17 -0
  134. package/build/src/cli/render/task.d.ts +2 -1
  135. package/build/src/cli/render/task.js +223 -60
  136. package/build/src/cli/render/terminal.d.ts +20 -0
  137. package/build/src/cli/render/terminal.js +121 -0
  138. package/build/src/cli/render/text.js +11 -3
  139. package/build/src/cli/result.d.ts +126 -19
  140. package/build/src/cli/usage.d.ts +1 -0
  141. package/build/src/cli/usage.js +3 -0
  142. package/build/src/contract-worktree.d.ts +44 -0
  143. package/build/src/contract-worktree.js +251 -0
  144. package/build/src/coordination/durable-file.d.ts +5 -1
  145. package/build/src/coordination/durable-file.js +57 -10
  146. package/build/src/coordination/sqlite-transaction-lock.d.ts +4 -0
  147. package/build/src/coordination/sqlite-transaction-lock.js +22 -5
  148. package/build/src/core/facts/codec.js +23 -4
  149. package/build/src/core/facts/eligibility.d.ts +1 -10
  150. package/build/src/core/facts/eligibility.js +0 -49
  151. package/build/src/core/facts/fold.js +1 -6
  152. package/build/src/core/facts/gate.d.ts +1 -0
  153. package/build/src/core/facts/gate.js +1 -0
  154. package/build/src/core/facts/observation.d.ts +3 -0
  155. package/build/src/core/facts/observation.js +12 -0
  156. package/build/src/core/facts/types.d.ts +10 -3
  157. package/build/src/core/subject.js +2 -2
  158. package/build/src/core/verbs/amend.d.ts +1 -1
  159. package/build/src/core/verbs/amend.js +3 -18
  160. package/build/src/core/verbs/bind.js +2 -16
  161. package/build/src/core/verbs/deliver.d.ts +1 -1
  162. package/build/src/core/verbs/deliver.js +14 -4
  163. package/build/src/core/verbs/placement.d.ts +1 -1
  164. package/build/src/core/verbs/placement.js +7 -9
  165. package/build/src/dispatch/index.d.ts +5 -3
  166. package/build/src/dispatch/index.js +30 -21
  167. package/build/src/duration.d.ts +10 -0
  168. package/build/src/duration.js +22 -0
  169. package/build/src/git/admission.d.ts +2 -2
  170. package/build/src/git/admission.js +33 -23
  171. package/build/src/git/hooks.d.ts +15 -0
  172. package/build/src/git/hooks.js +70 -18
  173. package/build/src/git/identity.d.ts +5 -1
  174. package/build/src/git/identity.js +23 -4
  175. package/build/src/git/integration.d.ts +48 -0
  176. package/build/src/git/integration.js +250 -0
  177. package/build/src/git/observe.d.ts +29 -15
  178. package/build/src/git/observe.js +185 -76
  179. package/build/src/git/read-observation.d.ts +41 -0
  180. package/build/src/git/read-observation.js +361 -0
  181. package/build/src/git/reconcile.d.ts +17 -5
  182. package/build/src/git/reconcile.js +305 -109
  183. package/build/src/git/repository.d.ts +41 -18
  184. package/build/src/git/repository.js +220 -114
  185. package/build/src/git/scratch.d.ts +17 -0
  186. package/build/src/git/scratch.js +73 -0
  187. package/build/src/git/target-placement.d.ts +92 -0
  188. package/build/src/git/target-placement.js +375 -0
  189. package/build/src/git/tender.d.ts +49 -0
  190. package/build/src/git/tender.js +63 -0
  191. package/build/src/git/terminal-seal.d.ts +24 -0
  192. package/build/src/git/terminal-seal.js +89 -0
  193. package/build/src/git/tree.d.ts +2 -2
  194. package/build/src/git/tree.js +4 -0
  195. package/build/src/git/workspace.d.ts +53 -0
  196. package/build/src/git/workspace.js +139 -0
  197. package/build/src/identity/selector.d.ts +6 -0
  198. package/build/src/identity/selector.js +19 -0
  199. package/build/src/index.d.ts +4 -2
  200. package/build/src/index.js +2 -1
  201. package/build/src/kanshi/index.d.ts +2 -2
  202. package/build/src/kanshi/index.js +1 -1
  203. package/build/src/kanshi/read.d.ts +7 -3
  204. package/build/src/kanshi/read.js +245 -45
  205. package/build/src/kanshi/report.d.ts +81 -6
  206. package/build/src/kanshi/select.d.ts +5 -0
  207. package/build/src/kanshi/select.js +33 -1
  208. package/build/src/library/address.d.ts +57 -0
  209. package/build/src/library/address.js +165 -0
  210. package/build/src/library/akuma-creation.d.ts +16 -7
  211. package/build/src/library/akuma-creation.js +96 -22
  212. package/build/src/library/audit.d.ts +18 -0
  213. package/build/src/library/audit.js +44 -0
  214. package/build/src/library/bind.d.ts +21 -0
  215. package/build/src/library/bind.js +56 -0
  216. package/build/src/library/catalog.d.ts +58 -0
  217. package/build/src/library/catalog.js +69 -0
  218. package/build/src/library/configuration.d.ts +6 -15
  219. package/build/src/library/configuration.js +14 -17
  220. package/build/src/library/contract.d.ts +23 -49
  221. package/build/src/library/contract.js +171 -185
  222. package/build/src/library/delivery.d.ts +24 -0
  223. package/build/src/library/delivery.js +21 -0
  224. package/build/src/library/fleet.d.ts +65 -0
  225. package/build/src/library/fleet.js +153 -0
  226. package/build/src/library/input.d.ts +3 -0
  227. package/build/src/library/input.js +19 -0
  228. package/build/src/library/keiyaku.d.ts +18 -3
  229. package/build/src/library/keiyaku.js +12 -1
  230. package/build/src/library/mutation.d.ts +39 -0
  231. package/build/src/library/mutation.js +37 -0
  232. package/build/src/library/reconcile.d.ts +29 -0
  233. package/build/src/library/reconcile.js +179 -0
  234. package/build/src/library/refusal.d.ts +21 -0
  235. package/build/src/library/refusal.js +29 -0
  236. package/build/src/library/region.d.ts +14 -1
  237. package/build/src/library/region.js +18 -4
  238. package/build/src/library/repo.d.ts +4 -13
  239. package/build/src/library/repo.js +14 -24
  240. package/build/src/protocol/attempt.d.ts +16 -3
  241. package/build/src/protocol/attempt.js +41 -10
  242. package/build/src/protocol/bind.d.ts +9 -2
  243. package/build/src/protocol/bind.js +48 -40
  244. package/build/src/protocol/intent.d.ts +57 -20
  245. package/build/src/protocol/intent.js +55 -79
  246. package/build/src/protocol/operations.d.ts +152 -43
  247. package/build/src/protocol/operations.js +482 -166
  248. package/build/src/protocol/outcome.d.ts +14 -3
  249. package/build/src/protocol/outcome.js +11 -3
  250. package/build/src/protocol/placement.d.ts +41 -0
  251. package/build/src/protocol/placement.js +147 -0
  252. package/build/src/protocol/read/documents.d.ts +2 -2
  253. package/build/src/protocol/read/documents.js +4 -4
  254. package/build/src/protocol/read/status.d.ts +18 -5
  255. package/build/src/protocol/read/status.js +73 -13
  256. package/build/src/protocol/run.d.ts +49 -7
  257. package/build/src/protocol/run.js +53 -25
  258. package/build/src/runtime/proc/line-rpc.d.ts +4 -8
  259. package/build/src/runtime/proc/line-rpc.js +22 -28
  260. package/build/src/runtime/proc/run.d.ts +23 -21
  261. package/build/src/runtime/proc/run.js +123 -89
  262. package/build/src/runtime/proc/stdio.d.ts +18 -0
  263. package/build/src/runtime/proc/stdio.js +58 -0
  264. package/build/src/settings.d.ts +7 -1
  265. package/build/src/settings.js +32 -17
  266. package/build/src/settlement/fence.d.ts +5 -0
  267. package/build/src/settlement/fence.js +14 -0
  268. package/build/src/settlement/holder.d.ts +35 -4
  269. package/build/src/settlement/holder.js +127 -28
  270. package/build/src/settlement/settle.d.ts +9 -1
  271. package/build/src/settlement/settle.js +74 -37
  272. package/build/src/task/board.d.ts +23 -5
  273. package/build/src/task/board.js +17 -10
  274. package/build/src/task/compose.d.ts +2 -2
  275. package/build/src/task/compose.js +18 -10
  276. package/build/src/task/context.d.ts +3 -3
  277. package/build/src/task/context.js +15 -20
  278. package/build/src/task/document.d.ts +2 -1
  279. package/build/src/task/document.js +15 -5
  280. package/build/src/task/index.d.ts +27 -15
  281. package/build/src/task/index.js +57 -35
  282. package/build/src/task/operations.d.ts +38 -13
  283. package/build/src/task/operations.js +80 -46
  284. package/build/src/task/query.d.ts +67 -0
  285. package/build/src/task/query.js +279 -0
  286. package/build/src/task/store.d.ts +5 -7
  287. package/build/src/task/store.js +43 -28
  288. package/build/src/verification/declaration.d.ts +1 -0
  289. package/build/src/verification/execution.d.ts +51 -0
  290. package/build/src/verification/execution.js +108 -0
  291. package/build/src/workspace-place.d.ts +41 -0
  292. package/build/src/workspace-place.js +386 -0
  293. package/build/src/world.d.ts +24 -0
  294. package/build/src/world.js +136 -0
  295. package/package.json +8 -4
  296. package/build/src/akuma/activity.d.ts +0 -66
  297. package/build/src/akuma/activity.js +0 -139
  298. package/build/src/akuma/providers/claude.js +0 -305
  299. package/build/src/context-root.d.ts +0 -4
  300. package/build/src/context-root.js +0 -15
  301. package/build/src/git/delivery.d.ts +0 -20
  302. package/build/src/git/delivery.js +0 -109
  303. package/build/src/git/verification.d.ts +0 -14
  304. package/build/src/git/verification.js +0 -56
  305. package/build/src/protocol/read/audit.d.ts +0 -25
  306. package/build/src/protocol/read/audit.js +0 -39
  307. package/build/src/verification/producer.d.ts +0 -23
  308. package/build/src/verification/producer.js +0 -54
@@ -39,6 +39,11 @@ function stringValue(value, path, nonblank = true) {
39
39
  }
40
40
  return value;
41
41
  }
42
+ function booleanValue(value, path) {
43
+ if (typeof value !== "boolean")
44
+ fail(path, "expected a boolean");
45
+ return value;
46
+ }
42
47
  function brandedValue(value, path, brand) {
43
48
  const text = stringValue(value, path);
44
49
  try {
@@ -156,11 +161,25 @@ function validateData(kind, value) {
156
161
  }
157
162
  case "deliver": {
158
163
  const object = requireRecord(value, path);
159
- requireKeys(object, ["expectedPredecessor", "candidate", "deliveryPatchId"], path);
164
+ requireKeys(object, ["tenderSnapshot", "integration", "method", "policy"], path);
165
+ const integration = requireRecord(object.integration, `${path}.integration`);
166
+ requireKeys(integration, ["predecessor", "snapshot", "changeId"], `${path}.integration`);
167
+ const method = stringValue(object.method, `${path}.method`);
168
+ if (method !== "squash")
169
+ fail(`${path}.method`, "unknown merge method");
170
+ const policy = requireRecord(object.policy, `${path}.policy`);
171
+ requireKeys(policy, ["requireBranchesToBeUpToDate"], `${path}.policy`);
160
172
  return {
161
- expectedPredecessor: brandedValue(object.expectedPredecessor, `${path}.expectedPredecessor`, snapshotId),
162
- candidate: brandedValue(object.candidate, `${path}.candidate`, snapshotId),
163
- deliveryPatchId: brandedValue(object.deliveryPatchId, `${path}.deliveryPatchId`, changeId),
173
+ tenderSnapshot: brandedValue(object.tenderSnapshot, `${path}.tenderSnapshot`, snapshotId),
174
+ integration: {
175
+ predecessor: brandedValue(integration.predecessor, `${path}.integration.predecessor`, snapshotId),
176
+ snapshot: brandedValue(integration.snapshot, `${path}.integration.snapshot`, snapshotId),
177
+ changeId: brandedValue(integration.changeId, `${path}.integration.changeId`, changeId),
178
+ },
179
+ method,
180
+ policy: {
181
+ requireBranchesToBeUpToDate: booleanValue(policy.requireBranchesToBeUpToDate, `${path}.policy.requireBranchesToBeUpToDate`),
182
+ },
164
183
  };
165
184
  }
166
185
  case "attestation":
@@ -1,16 +1,7 @@
1
- import type { Offer } from "./offer.js";
2
1
  import { type ContractsObservation } from "./observation.js";
3
- import type { ContractId, EntryUlid } from "./types.js";
2
+ import type { ContractId } from "./types.js";
4
3
  type EligibilityObservation = ContractsObservation;
5
- type EligibilityAttempt = Readonly<{
6
- entryUlids: readonly EntryUlid[];
7
- }>;
8
- type PrerequisiteStatus = "unknown" | "pending" | "claimed";
9
- /** Resolve prerequisite existence and eligibility from one decision snapshot. */
10
- export declare function prerequisiteStatus(prerequisites: readonly ContractId[], observation: EligibilityObservation, offeredClaims?: ReadonlySet<ContractId>): PrerequisiteStatus;
11
4
  /** Decide whether resulting prerequisites reach the amended contract. */
12
5
  export declare function prerequisitesReach(contract: ContractId, prerequisites: readonly ContractId[], observation: EligibilityObservation): boolean;
13
6
  export declare function samePrerequisites(left: readonly ContractId[], right: readonly ContractId[]): boolean;
14
- /** Append bounds made eligible by a claimed offer from its full snapshot. */
15
- export declare function placeEligibleBounds(offer: Offer, observation: EligibilityObservation, attempt: EligibilityAttempt): Offer;
16
7
  export {};
@@ -1,16 +1,4 @@
1
1
  import { contractState } from "./observation.js";
2
- /** Resolve prerequisite existence and eligibility from one decision snapshot. */
3
- export function prerequisiteStatus(prerequisites, observation, offeredClaims) {
4
- let pending = false;
5
- for (const dependency of prerequisites) {
6
- const state = contractState(observation, dependency);
7
- if (state === null)
8
- return "unknown";
9
- if (state.terminal?.kind !== "claimed" && offeredClaims?.has(dependency) !== true)
10
- pending = true;
11
- }
12
- return pending ? "pending" : "claimed";
13
- }
14
2
  /** Decide whether resulting prerequisites reach the amended contract. */
15
3
  export function prerequisitesReach(contract, prerequisites, observation) {
16
4
  const pending = [...prerequisites];
@@ -36,40 +24,3 @@ export function samePrerequisites(left, right) {
36
24
  return false;
37
25
  return left.every((id, index) => id === right[index]);
38
26
  }
39
- /** Append bounds made eligible by a claimed offer from its full snapshot. */
40
- export function placeEligibleBounds(offer, observation, attempt) {
41
- const claimedEntry = offer.facts[0]?.entries[0];
42
- if (offer.facts.length !== 1 || offer.facts[0]?.entries.length !== 1 || claimedEntry?.kind !== "claimed") {
43
- throw new Error("eligible-bound placement requires exactly one claimed fact");
44
- }
45
- const claimed = new Set([claimedEntry.contract]);
46
- const used = new Set([claimedEntry.entry]);
47
- const available = attempt.entryUlids.filter((entry) => !used.has(entry));
48
- let availableIndex = 0;
49
- const additions = [];
50
- for (const id of observation.keys()) {
51
- const state = contractState(observation, id);
52
- if (!state || state.bound || state.terminal)
53
- continue;
54
- if (prerequisiteStatus(state.terms.after, observation, claimed) !== "claimed")
55
- continue;
56
- const entry = available[availableIndex];
57
- if (entry === undefined)
58
- throw new Error("placement attempt lacks an entry ULID for an eligible contract");
59
- availableIndex += 1;
60
- additions.push({
61
- contractId: id,
62
- expectedHead: state.head,
63
- entries: [{
64
- v: 1,
65
- kind: "bound",
66
- contract: id,
67
- entry,
68
- at: claimedEntry.at,
69
- ...(claimedEntry.actor === undefined ? {} : { actor: claimedEntry.actor }),
70
- data: {},
71
- }],
72
- });
73
- }
74
- return additions.length === 0 ? offer : { ...offer, facts: [...additions, ...offer.facts] };
75
- }
@@ -1,4 +1,3 @@
1
- import { samePrerequisites } from "./eligibility.js";
2
1
  import { gatesSatisfied } from "./gate.js";
3
2
  import { AuthorityCorruptionError } from "./errors.js";
4
3
  function foldError(message) {
@@ -47,12 +46,8 @@ function foldEntry(state, entry, attestations) {
47
46
  switch (entry.kind) {
48
47
  case "bind":
49
48
  foldError("bind may appear only once");
50
- case "amend": {
51
- if (state.bound !== null && !samePrerequisites(state.terms.after, entry.data.after)) {
52
- foldError("cannot change after once prerequisites are consumed");
53
- }
49
+ case "amend":
54
50
  return { ...state, terms: cloneTerms(entry.data) };
55
- }
56
51
  case "bound":
57
52
  if (state.bound !== null)
58
53
  foldError("bound may appear only once");
@@ -3,6 +3,7 @@ export type GateCurrent = Readonly<{
3
3
  kind: "attested";
4
4
  verdict: "satisfied" | "unsatisfied";
5
5
  summary?: string;
6
+ at: string;
6
7
  }> | Readonly<{
7
8
  kind: "stale";
8
9
  priorVerdict: "satisfied" | "unsatisfied";
@@ -37,6 +37,7 @@ export function gateReports(state) {
37
37
  kind: "attested",
38
38
  verdict: attestation.data.verdict,
39
39
  ...(attestation.data.summary === undefined ? {} : { summary: attestation.data.summary }),
40
+ at: attestation.at,
40
41
  },
41
42
  };
42
43
  }
@@ -1,8 +1,11 @@
1
1
  import type { ContractId, ContractState, DocumentKey } from "./types.js";
2
2
  /** The complete state projection consumed by one pure pact decision. */
3
3
  export type ContractsObservation = ReadonlyMap<ContractId, ContractState | null>;
4
+ export type PrerequisiteStatus = "unknown" | "pending" | "claimed";
4
5
  /** Read one requested identity; a missing key violates the observation contract. */
5
6
  export declare function contractState(observation: ContractsObservation, contract: ContractId): ContractState | null;
7
+ /** Project prerequisite existence and terminal satisfaction from one observation. */
8
+ export declare function prerequisiteStatus(prerequisites: readonly ContractId[], observation: ContractsObservation): PrerequisiteStatus;
6
9
  export type ActiveContractRefusal = Readonly<{
7
10
  kind: "contract-missing" | "terminal";
8
11
  contractId: ContractId;
@@ -9,6 +9,18 @@ export function contractState(observation, contract) {
9
9
  }
10
10
  return state;
11
11
  }
12
+ /** Project prerequisite existence and terminal satisfaction from one observation. */
13
+ export function prerequisiteStatus(prerequisites, observation) {
14
+ let pending = false;
15
+ for (const dependency of prerequisites) {
16
+ const state = contractState(observation, dependency);
17
+ if (state === null)
18
+ return "unknown";
19
+ if (state.terminal?.kind !== "claimed")
20
+ pending = true;
21
+ }
22
+ return pending ? "pending" : "claimed";
23
+ }
12
24
  /** The single lifecycle guard shared by every operation on an existing contract. */
13
25
  export function activeContract(observation, contract) {
14
26
  const state = contractState(observation, contract);
@@ -74,9 +74,16 @@ export type BindData = Readonly<{
74
74
  export type AmendData = ContractTerms;
75
75
  export type BoundData = Readonly<Record<string, never>>;
76
76
  export type DeliverData = Readonly<{
77
- expectedPredecessor: SnapshotId;
78
- candidate: SnapshotId;
79
- deliveryPatchId: ChangeId;
77
+ tenderSnapshot: SnapshotId;
78
+ integration: Readonly<{
79
+ predecessor: SnapshotId;
80
+ snapshot: SnapshotId;
81
+ changeId: ChangeId;
82
+ }>;
83
+ method: "squash";
84
+ policy: Readonly<{
85
+ requireBranchesToBeUpToDate: boolean;
86
+ }>;
80
87
  }>;
81
88
  export type AttestationData = Readonly<{
82
89
  gate: Gate;
@@ -69,8 +69,8 @@ function currentKeys(state) {
69
69
  encodeKey({ kind: "document", value: state.terms.document.key }),
70
70
  ...state.terms.segments.map((value) => encodeKey({ kind: "segment", value })),
71
71
  ...(state.delivery === null ? [] : [
72
- encodeKey({ kind: "snapshot", value: state.delivery.data.candidate }),
73
- encodeKey({ kind: "change", value: state.delivery.data.deliveryPatchId }),
72
+ encodeKey({ kind: "snapshot", value: state.delivery.data.integration.snapshot }),
73
+ encodeKey({ kind: "change", value: state.delivery.data.integration.changeId }),
74
74
  ]),
75
75
  ]);
76
76
  }
@@ -8,7 +8,7 @@ export type AmendInput<Failure = never> = Readonly<{
8
8
  preparation?: Preparation<AmendData, Failure>;
9
9
  }>;
10
10
  export type AmendRefusal = Readonly<{
11
- kind: "contract-missing" | "terminal" | "terms-moved" | "prerequisites-already-consumed" | "unknown-prerequisite" | "cyclic-prerequisite";
11
+ kind: "contract-missing" | "terminal" | "terms-moved" | "unknown-prerequisite" | "cyclic-prerequisite";
12
12
  contractId: ContractId;
13
13
  }>;
14
14
  export declare function decideAmend<Failure>({ input, attempt, observation }: DecideInput<AmendInput<Failure>>): OfferDecision<AmendRefusal | Failure>;
@@ -1,5 +1,5 @@
1
- import { prerequisiteStatus, prerequisitesReach, samePrerequisites } from "../facts/eligibility.js";
2
- import { activeContract } from "../facts/observation.js";
1
+ import { prerequisitesReach, samePrerequisites } from "../facts/eligibility.js";
2
+ import { activeContract, prerequisiteStatus } from "../facts/observation.js";
3
3
  export function decideAmend({ input, attempt, observation }) {
4
4
  const id = input.contractId;
5
5
  const current = activeContract(observation, id);
@@ -21,9 +21,6 @@ export function decideAmend({ input, attempt, observation }) {
21
21
  if (input.preparation.kind === "refused")
22
22
  return { kind: "refused", refusal: input.preparation.refusal };
23
23
  const { data } = input.preparation;
24
- if (current.bound !== null && !samePrerequisites(current.terms.after, data.after)) {
25
- return { kind: "refused", refusal: { kind: "prerequisites-already-consumed", contractId: id } };
26
- }
27
24
  const prerequisites = prerequisiteStatus(data.after, observation);
28
25
  if (prerequisites === "unknown") {
29
26
  return { kind: "refused", refusal: { kind: "unknown-prerequisite", contractId: id } };
@@ -40,17 +37,5 @@ export function decideAmend({ input, attempt, observation }) {
40
37
  ...(input.actor === undefined ? {} : { actor: input.actor }),
41
38
  data,
42
39
  };
43
- const entries = [entry];
44
- if (current.bound === null && prerequisites === "claimed") {
45
- entries.push({
46
- v: 1,
47
- kind: "bound",
48
- contract: id,
49
- entry: attempt.entryUlids[1],
50
- at: input.at,
51
- ...(input.actor === undefined ? {} : { actor: input.actor }),
52
- data: {},
53
- });
54
- }
55
- return { kind: "offer", offer: { facts: [{ contractId: id, expectedHead: current.head, entries }] } };
40
+ return { kind: "offer", offer: { facts: [{ contractId: id, expectedHead: current.head, entries: [entry] }] } };
56
41
  }
@@ -1,5 +1,4 @@
1
- import { prerequisiteStatus } from "../facts/eligibility.js";
2
- import { contractState } from "../facts/observation.js";
1
+ import { contractState, prerequisiteStatus } from "../facts/observation.js";
3
2
  export function decideBind({ input, attempt, observation }) {
4
3
  const id = input.contractId;
5
4
  const current = contractState(observation, id);
@@ -23,18 +22,5 @@ export function decideBind({ input, attempt, observation }) {
23
22
  ...(input.actor === undefined ? {} : { actor: input.actor }),
24
23
  data,
25
24
  };
26
- const entries = [bind];
27
- if (prerequisites === "claimed") {
28
- const bound = {
29
- v: 1,
30
- kind: "bound",
31
- contract: id,
32
- entry: attempt.entryUlids[1],
33
- at: input.at,
34
- ...(input.actor === undefined ? {} : { actor: input.actor }),
35
- data: {},
36
- };
37
- entries.push(bound);
38
- }
39
- return { kind: "offer", offer: { facts: [{ contractId: id, expectedHead: null, entries }] } };
25
+ return { kind: "offer", offer: { facts: [{ contractId: id, expectedHead: null, entries: [bind] }] } };
40
26
  }
@@ -7,7 +7,7 @@ export type DeliverInput<Failure = never> = Readonly<{
7
7
  preparation: StampedPreparation<DeliverData, Failure>;
8
8
  }>;
9
9
  export type DeliverRefusal = Readonly<{
10
- kind: "contract-missing" | "not-bound" | "terminal" | "document-moved";
10
+ kind: "contract-missing" | "terminal" | "document-moved";
11
11
  contractId: ContractId;
12
12
  }>;
13
13
  export declare function decideDeliver<Failure>({ input, attempt, observation, }: DecideInput<DeliverInput<Failure>>): OfferDecision<DeliverRefusal | Failure>;
@@ -3,8 +3,6 @@ export function decideDeliver({ input, attempt, observation, }) {
3
3
  const state = activeContract(observation, input.contractId);
4
4
  if ("kind" in state)
5
5
  return { kind: "refused", refusal: state };
6
- if (state.bound === null)
7
- return { kind: "refused", refusal: { kind: "not-bound", contractId: input.contractId } };
8
6
  if (input.preparation.kind === "unavailable") {
9
7
  throw new Error("existing contract requires a stamped delivery preparation");
10
8
  }
@@ -13,14 +11,26 @@ export function decideDeliver({ input, attempt, observation, }) {
13
11
  }
14
12
  if (input.preparation.kind === "refused")
15
13
  return { kind: "refused", refusal: input.preparation.refusal };
14
+ const entries = [];
15
+ if (state.bound === null)
16
+ entries.push({
17
+ v: 1,
18
+ kind: "bound",
19
+ contract: input.contractId,
20
+ entry: attempt.entryUlids[0],
21
+ at: input.at,
22
+ ...(input.actor === undefined ? {} : { actor: input.actor }),
23
+ data: {},
24
+ });
16
25
  const deliver = {
17
26
  v: 1,
18
27
  kind: "deliver",
19
28
  contract: input.contractId,
20
- entry: attempt.entryUlids[0],
29
+ entry: attempt.entryUlids[state.bound === null ? 1 : 0],
21
30
  at: input.at,
22
31
  ...(input.actor === undefined ? {} : { actor: input.actor }),
23
32
  data: input.preparation.data,
24
33
  };
25
- return { kind: "offer", offer: { facts: [{ contractId: input.contractId, expectedHead: state.head, entries: [deliver] }] } };
34
+ entries.push(deliver);
35
+ return { kind: "offer", offer: { facts: [{ contractId: input.contractId, expectedHead: state.head, entries }] } };
26
36
  }
@@ -6,7 +6,7 @@ type PlacementInput = Readonly<{
6
6
  at: string;
7
7
  }>;
8
8
  export type PlacementRefusal = Readonly<{
9
- kind: "contract-missing" | "delivery-missing" | "terminal" | "gates-unsatisfied";
9
+ kind: "contract-missing" | "delivery-missing" | "terminal" | "prerequisites-unsatisfied" | "gates-unsatisfied";
10
10
  contractId: ContractId;
11
11
  }>;
12
12
  export declare function decidePlacement({ input, attempt, observation }: DecideInput<PlacementInput>): OfferDecision<PlacementRefusal>;
@@ -1,6 +1,5 @@
1
1
  import { gatesSatisfied } from "../facts/gate.js";
2
- import { placeEligibleBounds } from "../facts/eligibility.js";
3
- import { activeContract } from "../facts/observation.js";
2
+ import { activeContract, prerequisiteStatus } from "../facts/observation.js";
4
3
  export function decidePlacement({ input, attempt, observation }) {
5
4
  const id = input.contractId;
6
5
  const current = activeContract(observation, id);
@@ -10,6 +9,9 @@ export function decidePlacement({ input, attempt, observation }) {
10
9
  if (!delivery) {
11
10
  return { kind: "refused", refusal: { kind: "delivery-missing", contractId: id } };
12
11
  }
12
+ if (prerequisiteStatus(current.terms.after, observation) !== "claimed") {
13
+ return { kind: "refused", refusal: { kind: "prerequisites-unsatisfied", contractId: id } };
14
+ }
13
15
  if (!gatesSatisfied(current)) {
14
16
  return { kind: "refused", refusal: { kind: "gates-unsatisfied", contractId: id } };
15
17
  }
@@ -22,21 +24,17 @@ export function decidePlacement({ input, attempt, observation }) {
22
24
  ...(input.actor === undefined ? {} : { actor: input.actor }),
23
25
  data: { delivery: delivery.entry },
24
26
  };
25
- const offer = {
27
+ return {
26
28
  kind: "offer",
27
29
  offer: {
28
30
  facts: [{ contractId: id, expectedHead: current.head, entries: [claimed] }],
29
31
  ...(current.coordinates.target === undefined ? {} : {
30
32
  target: {
31
33
  target: current.coordinates.target,
32
- expectedOid: delivery.data.expectedPredecessor,
33
- newOid: delivery.data.candidate,
34
+ expectedOid: delivery.data.integration.predecessor,
35
+ newOid: delivery.data.integration.snapshot,
34
36
  },
35
37
  }),
36
38
  },
37
39
  };
38
- return {
39
- ...offer,
40
- offer: placeEligibleBounds(offer.offer, observation, attempt),
41
- };
42
40
  }
@@ -1,6 +1,7 @@
1
1
  import { type ContractId } from "../core/facts/types.js";
2
2
  import { type AkuId } from "../akuma/identity.js";
3
3
  import { type GitRepository } from "../git/repository.js";
4
+ import { type GitReadObservation } from "../git/read-observation.js";
4
5
  export type Dispatch = Readonly<{
5
6
  akuId: AkuId;
6
7
  contractId: ContractId;
@@ -22,10 +23,11 @@ export type DispatchPublication = Readonly<{
22
23
  kind: "failed";
23
24
  failure: DispatchFailure;
24
25
  }>;
25
- export declare function readDispatch(repository: GitRepository, value: AkuId): Dispatch | null;
26
- export declare function readDispatches(repository: GitRepository): readonly Dispatch[];
26
+ export declare function readDispatch(repository: GitRepository, value: AkuId): Promise<Dispatch | null>;
27
+ export declare function readDispatchesAt(observation: GitReadObservation): Promise<readonly Dispatch[]>;
28
+ export declare function readDispatches(repository: GitRepository): Promise<readonly Dispatch[]>;
27
29
  export declare function publishDispatch(input: Readonly<{
28
30
  repository: GitRepository;
29
31
  akuId: AkuId;
30
32
  contractId: ContractId;
31
- }>): DispatchPublication;
33
+ }>): Promise<DispatchPublication>;
@@ -2,7 +2,8 @@ import { createHash } from "node:crypto";
2
2
  import { AuthorityCorruptionError } from "../core/facts/errors.js";
3
3
  import { contractId } from "../core/facts/types.js";
4
4
  import { parseAkuId } from "../akuma/identity.js";
5
- import { GIT_FORMAT_BYTES, GIT_FORMAT_PATH, GIT_REF, readBlob, readGit, readGitPaths, updateGitTree, updateRefsAtomically, writeBlob, writeCommit, } from "../git/repository.js";
5
+ import { GIT_FORMAT_BYTES, GIT_FORMAT_PATH, GIT_REF, readBlob, readGitPaths, updateGitTree, updateRefsAtomically, writeBlob, writeCommit, } from "../git/repository.js";
6
+ import { withGitDecodeChannel, withGitReadObservation } from "../git/read-observation.js";
6
7
  const DISPATCH_ROOT = "dispatch";
7
8
  const DISPATCH_PREFIX = `${DISPATCH_ROOT}/`;
8
9
  const DISPATCH_SUFFIX = ".json";
@@ -63,25 +64,27 @@ function decode(path, bytes) {
63
64
  }
64
65
  return dispatch;
65
66
  }
66
- function dispatchFromSnapshot(repository, snapshot, akuId) {
67
+ async function dispatchFromSnapshot(repository, snapshot, akuId) {
67
68
  const path = pathFor(akuId);
68
69
  const entry = snapshot.paths.get(path);
69
70
  if (entry === undefined)
70
71
  return null;
71
72
  if (entry.type !== "blob")
72
73
  corruption(`Dispatch path is not a blob: ${path}`);
73
- return decode(path, readBlob(repository, entry.oid));
74
+ return decode(path, await readBlob(repository, entry.oid));
74
75
  }
75
- export function readDispatch(repository, value) {
76
+ export async function readDispatch(repository, value) {
76
77
  const akuId = parseAkuId(value).id;
77
78
  const path = pathFor(akuId);
78
- return dispatchFromSnapshot(repository, readGitPaths(repository, [path]), akuId);
79
+ return await dispatchFromSnapshot(repository, await readGitPaths(repository, [path]), akuId);
79
80
  }
80
- export function readDispatches(repository) {
81
- const snapshot = readGit(repository);
81
+ export async function readDispatchesAt(observation) {
82
+ const entries = [...observation.snapshot.paths]
83
+ .filter(([path, entry]) => path.startsWith(DISPATCH_PREFIX) && entry.type === "blob");
84
+ const blobs = await observation.readBlobs(entries.map(([, entry]) => entry.oid));
82
85
  const dispatches = [];
83
86
  const seen = new Set();
84
- for (const [path, entry] of snapshot.paths) {
87
+ for (const [path, entry] of observation.snapshot.paths) {
85
88
  if (path === DISPATCH_ROOT)
86
89
  corruption(`Dispatch authority root is not a tree: ${path}`);
87
90
  if (!path.startsWith(DISPATCH_PREFIX))
@@ -90,7 +93,10 @@ export function readDispatches(repository) {
90
93
  corruption(`unexpected Dispatch authority path: ${path}`);
91
94
  if (entry.type !== "blob")
92
95
  corruption(`Dispatch path is not a blob: ${path}`);
93
- const dispatch = decode(path, readBlob(repository, entry.oid));
96
+ const result = blobs.get(entry.oid);
97
+ if (result?.kind !== "present")
98
+ corruption(`missing Dispatch Git object: ${path}`);
99
+ const dispatch = decode(path, result.bytes);
94
100
  if (seen.has(dispatch.akuId))
95
101
  corruption(`duplicate Dispatch identity: ${dispatch.akuId}`);
96
102
  seen.add(dispatch.akuId);
@@ -98,8 +104,11 @@ export function readDispatches(repository) {
98
104
  }
99
105
  return dispatches.sort((left, right) => Buffer.compare(Buffer.from(left.akuId), Buffer.from(right.akuId)));
100
106
  }
101
- function observedPublication(repository, akuId, owner) {
102
- const current = readDispatch(repository, akuId);
107
+ export async function readDispatches(repository) {
108
+ return await withGitDecodeChannel(repository, (channel) => withGitReadObservation(repository, channel, readDispatchesAt));
109
+ }
110
+ async function observedPublication(repository, akuId, owner) {
111
+ const current = await readDispatch(repository, akuId);
103
112
  if (current === null)
104
113
  return null;
105
114
  return current.contractId === owner
@@ -109,14 +118,14 @@ function observedPublication(repository, akuId, owner) {
109
118
  function diagnostic(error) {
110
119
  return error instanceof Error ? error.message : String(error);
111
120
  }
112
- export function publishDispatch(input) {
121
+ export async function publishDispatch(input) {
113
122
  const akuId = parseAkuId(input.akuId).id;
114
123
  const owner = contractId(input.contractId);
115
124
  const intended = { akuId, contractId: owner, dispatchedAt: new Date().toISOString() };
116
125
  const path = pathFor(akuId);
117
126
  for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt += 1) {
118
- const snapshot = readGitPaths(input.repository, [path]);
119
- const current = dispatchFromSnapshot(input.repository, snapshot, akuId);
127
+ const snapshot = await readGitPaths(input.repository, [path]);
128
+ const current = await dispatchFromSnapshot(input.repository, snapshot, akuId);
120
129
  if (current !== null) {
121
130
  return current.contractId === owner
122
131
  ? { kind: "dispatched", dispatch: current }
@@ -124,29 +133,29 @@ export function publishDispatch(input) {
124
133
  }
125
134
  const changes = new Map();
126
135
  if (snapshot.commit === null) {
127
- changes.set(GIT_FORMAT_PATH, { oid: writeBlob(input.repository, GIT_FORMAT_BYTES) });
136
+ changes.set(GIT_FORMAT_PATH, { oid: await writeBlob(input.repository, GIT_FORMAT_BYTES) });
128
137
  }
129
- changes.set(path, { oid: writeBlob(input.repository, bytesFor(intended)) });
130
- const tree = updateGitTree(input.repository, snapshot.tree, changes);
131
- const commit = writeCommit({
138
+ changes.set(path, { oid: await writeBlob(input.repository, bytesFor(intended)) });
139
+ const tree = await updateGitTree(input.repository, snapshot.tree, changes);
140
+ const commit = await writeCommit({
132
141
  repository: input.repository,
133
142
  tree,
134
143
  parent: snapshot.commit,
135
144
  message: `dispatch ${akuId}`,
136
145
  at: intended.dispatchedAt,
137
146
  });
138
- const publication = updateRefsAtomically(input.repository, [{
147
+ const publication = await updateRefsAtomically(input.repository, [{
139
148
  ref: GIT_REF,
140
149
  newOid: commit,
141
150
  expectedOid: snapshot.commit,
142
151
  }]);
143
152
  if (publication.kind === "published")
144
153
  return { kind: "dispatched", dispatch: intended };
145
- const observed = observedPublication(input.repository, akuId, owner);
154
+ const observed = await observedPublication(input.repository, akuId, owner);
146
155
  if (observed !== null)
147
156
  return observed;
148
157
  if (publication.kind === "non-published") {
149
- const fresh = readGitPaths(input.repository, [path]);
158
+ const fresh = await readGitPaths(input.repository, [path]);
150
159
  if (fresh.commit === snapshot.commit) {
151
160
  return {
152
161
  kind: "failed",
@@ -0,0 +1,10 @@
1
+ export type DurationParseResult = Readonly<{
2
+ kind: "parsed";
3
+ milliseconds: number;
4
+ }> | Readonly<{
5
+ kind: "invalid";
6
+ }> | Readonly<{
7
+ kind: "overflow";
8
+ }>;
9
+ export declare function parseDuration(value: string): DurationParseResult;
10
+ export declare function formatDuration(milliseconds: number): string;
@@ -0,0 +1,22 @@
1
+ const MULTIPLIERS = Object.freeze({
2
+ ms: 1n,
3
+ s: 1000n,
4
+ m: 60000n,
5
+ h: 3600000n,
6
+ });
7
+ export function parseDuration(value) {
8
+ const match = /^(0|[1-9][0-9]*)(ms|s|m|h)$/u.exec(value);
9
+ if (match === null)
10
+ return { kind: "invalid" };
11
+ const milliseconds = BigInt(match[1]) * MULTIPLIERS[match[2]];
12
+ if (milliseconds > BigInt(Number.MAX_SAFE_INTEGER))
13
+ return { kind: "overflow" };
14
+ return { kind: "parsed", milliseconds: Number(milliseconds) };
15
+ }
16
+ export function formatDuration(milliseconds) {
17
+ for (const [unit, multiplier] of [["h", 3_600_000], ["m", 60_000], ["s", 1_000]]) {
18
+ if (milliseconds >= multiplier && milliseconds % multiplier === 0)
19
+ return `${milliseconds / multiplier}${unit}`;
20
+ }
21
+ return `${milliseconds}ms`;
22
+ }
@@ -1,6 +1,6 @@
1
1
  import type { Offer } from "../core/facts/offer.js";
2
2
  import type { GitAdmissionSnapshot } from "./observe.js";
3
- import { type GitRepository } from "./repository.js";
3
+ import { type GitRepository, type GitRefAssertion } from "./repository.js";
4
4
  import { type ContractHead } from "../core/facts/types.js";
5
5
  type Accepted = Readonly<{
6
6
  kind: "accepted";
@@ -14,5 +14,5 @@ type Unknown = Readonly<{
14
14
  kind: "unknown";
15
15
  }>;
16
16
  export type Admission = Accepted | PublicationFailed | Unknown;
17
- export declare function admit(repository: GitRepository, offer: Offer, admission: GitAdmissionSnapshot): Admission;
17
+ export declare function admit(repository: GitRepository, offer: Offer, admission: GitAdmissionSnapshot, assertions?: readonly GitRefAssertion[]): Promise<Admission>;
18
18
  export {};