@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
@@ -1,18 +1,22 @@
1
1
  import { documentDiff } from "../markdown/diff.js";
2
2
  import { createTaskRelations, projectDetailFacts, projectTaskBoardObservation, relationProblem, projectBlocked, projectRows, } from "./board.js";
3
- import { parseTaskCreationDocument, serializeTaskDocument } from "./document.js";
3
+ import { parseTaskCreationDocument, serializeTaskDocument, } from "./document.js";
4
4
  import { allocateLocalId, deriveLocalStem, formatTaskId, parseTaskId, sameNamespace } from "./identity.js";
5
- import { authorityPath, nukeTaskAuthority, readBoard, replaceAuthority, withTaskLocks, } from "./store.js";
6
- import { DEFAULT_TASK_LIMIT, projectPage, projectQuery, queryUnderTargets, underExpression } from "./query.js";
7
- export async function nukeTask(world) {
8
- if (await nukeTaskAuthority(world) === "busy")
5
+ import { authorityPath, nukeTaskAuthority, readBoard, replaceAuthority, withTaskLocks } from "./store.js";
6
+ import { DEFAULT_TASK_LIMIT, projectPage, projectQuery, queryUnderTargets, underExpression, } from "./query.js";
7
+ export async function nukeTask(world, options) {
8
+ if ((await nukeTaskAuthority(world, options)) === "busy")
9
9
  throw new Error("Task reset lock contention");
10
10
  }
11
11
  export function taskView(document) {
12
12
  return { ...document, namespace: parseTaskId(document.id).namespace };
13
13
  }
14
- function refused(refusal) { return { kind: "refused", refusal }; }
15
- function retry(reason) { return { kind: "retry", reason }; }
14
+ function refused(refusal) {
15
+ return { kind: "refused", refusal };
16
+ }
17
+ function retry(reason) {
18
+ return { kind: "retry", reason };
19
+ }
16
20
  function occupied(board, namespace) {
17
21
  return new Set([...board.tasks.values()].flatMap((task) => {
18
22
  const coordinate = parseTaskId(task.id);
@@ -24,16 +28,16 @@ function currentTimestamp() {
24
28
  }
25
29
  /** Internal Task timestamp rule shared by ordinary mutations and compose. */
26
30
  export function advanceTaskTimestamp(previous, candidate) {
27
- return candidate > previous
28
- ? candidate
29
- : new Date(Date.parse(previous) + 1).toISOString();
31
+ return candidate > previous ? candidate : new Date(Date.parse(previous) + 1).toISOString();
30
32
  }
31
33
  function addDocument(base, namespace, board, at, actor) {
32
34
  const localId = allocateLocalId(deriveLocalStem(base.title), occupied(board, namespace));
33
35
  return {
34
- ...base, id: formatTaskId({ namespace, localId }),
36
+ ...base,
37
+ id: formatTaskId({ namespace, localId }),
35
38
  ...(actor === undefined ? {} : { createdBy: actor }),
36
- createdAt: at, updatedAt: at,
39
+ createdAt: at,
40
+ updatedAt: at,
37
41
  };
38
42
  }
39
43
  function boardWith(board, document) {
@@ -47,16 +51,29 @@ async function create(world, base, namespace, signal, actor) {
47
51
  const problem = relationProblem(boardWith(snapshot.board, next), null, next);
48
52
  if (problem !== null)
49
53
  return refused({ kind: "invalid-graph", diagnostic: problem });
50
- const replaced = await replaceAuthority({ path: authorityPath(world, next.id), expected: null, next: serializeTaskDocument(next) });
51
- return replaced === "replaced" ? { kind: "accepted", value: taskView(next) } : retry("concurrent-modification");
54
+ const replaced = await replaceAuthority({
55
+ path: authorityPath(world, next.id),
56
+ expected: null,
57
+ next: serializeTaskDocument(next),
58
+ });
59
+ return replaced === "replaced"
60
+ ? { kind: "accepted", value: taskView(next) }
61
+ : retry("concurrent-modification");
52
62
  });
53
63
  return result === "busy" ? retry("busy") : result;
54
64
  }
55
65
  export async function addTask(world, input) {
56
66
  const namespace = input.namespace ?? [];
57
67
  return create(world, {
58
- title: input.title, body: input.body ?? "", note: input.note ?? "", state: input.state ?? "open", priority: input.priority ?? 2, needs: input.needs ?? [], parent: input.parent ?? null,
59
- supersedes: input.supersedes ?? [], relates: input.relates ?? [],
68
+ title: input.title,
69
+ body: input.body ?? "",
70
+ note: input.note ?? "",
71
+ state: input.state ?? "open",
72
+ priority: input.priority ?? 2,
73
+ needs: input.needs ?? [],
74
+ parent: input.parent ?? null,
75
+ supersedes: input.supersedes ?? [],
76
+ relates: input.relates ?? [],
60
77
  }, namespace, input.signal, input.actor);
61
78
  }
62
79
  export async function addTaskDocument(world, input) {
@@ -73,6 +90,12 @@ function listChange(current, replacement, additions, removals) {
73
90
  function relatedOwner(board, id, target) {
74
91
  return board.tasks.get(target)?.relates.includes(id) ? target : null;
75
92
  }
93
+ function appendTaskBody(current, addition) {
94
+ if (current.length === 0 || addition.length === 0 || current.endsWith("\n") || addition.startsWith("\n")) {
95
+ return current + addition;
96
+ }
97
+ return `${current}\n${addition}`;
98
+ }
76
99
  function updateDocument(board, current, input) {
77
100
  for (const related of input.dropRelates ?? [])
78
101
  if (!current.relates.includes(related)) {
@@ -83,7 +106,7 @@ function updateDocument(board, current, input) {
83
106
  return {
84
107
  ...current,
85
108
  ...(input.title === undefined ? {} : { title: input.title }),
86
- body: input.body ?? (input.appendBody === undefined ? current.body : current.body + input.appendBody),
109
+ body: input.body ?? (input.appendBody === undefined ? current.body : appendTaskBody(current.body, input.appendBody)),
87
110
  ...(input.note === undefined ? {} : { note: input.note }),
88
111
  ...(input.priority === undefined ? {} : { priority: input.priority }),
89
112
  needs: listChange(current.needs, input.needs, input.addNeeds, input.dropNeeds),
@@ -113,18 +136,25 @@ export async function updateTask(world, id, input) {
113
136
  }
114
137
  const afterBytes = serializeTaskDocument(next);
115
138
  const after = Buffer.from(afterBytes).toString("utf8");
116
- if (before !== after
117
- && await replaceAuthority({ path: authorityPath(world, id), expected: predecessor, next: afterBytes }) !== "replaced") {
139
+ if (before !== after &&
140
+ (await replaceAuthority({ path: authorityPath(world, id), expected: predecessor, next: afterBytes })) !==
141
+ "replaced") {
118
142
  return { kind: "retry", reason: "concurrent-modification" };
119
143
  }
120
144
  const label = `${id}.md`;
121
- return { kind: "accepted", value: { task: taskView(next), documentDiff: documentDiff(label, label, before, after) } };
145
+ return {
146
+ kind: "accepted",
147
+ value: { task: taskView(next), documentDiff: documentDiff(label, label, before, after) },
148
+ };
122
149
  });
123
150
  return result === "busy" ? { kind: "retry", reason: "busy" } : result;
124
151
  }
125
152
  const TRANSITIONS = {
126
- start: { open: "in_progress" }, stop: { in_progress: "open" }, hold: { open: "on_hold", in_progress: "on_hold" },
127
- resume: { on_hold: "open" }, done: { open: "done", in_progress: "done", on_hold: "done" },
153
+ start: { open: "in_progress" },
154
+ stop: { in_progress: "open" },
155
+ hold: { open: "on_hold", in_progress: "on_hold" },
156
+ resume: { on_hold: "open" },
157
+ done: { open: "done", in_progress: "done", on_hold: "done" },
128
158
  drop: { open: "drop", in_progress: "drop", on_hold: "drop" },
129
159
  };
130
160
  export async function lifecycleTask(world, id, verb, signal, note) {
@@ -144,8 +174,13 @@ export async function lifecycleTask(world, id, verb, signal, note) {
144
174
  updatedAt: advanceTaskTimestamp(current.updatedAt, at),
145
175
  };
146
176
  const bytes = serializeTaskDocument(next);
147
- return await replaceAuthority({ path: authorityPath(world, id), expected: snapshot.bytes.get(id), next: bytes }) === "replaced"
148
- ? { kind: "accepted", value: taskView(next) } : retry("concurrent-modification");
177
+ return (await replaceAuthority({
178
+ path: authorityPath(world, id),
179
+ expected: snapshot.bytes.get(id),
180
+ next: bytes,
181
+ })) === "replaced"
182
+ ? { kind: "accepted", value: taskView(next) }
183
+ : retry("concurrent-modification");
149
184
  });
150
185
  return result === "busy" ? retry("busy") : result;
151
186
  }
@@ -165,7 +200,10 @@ export async function settleTask(world, id) {
165
200
  if (current.state === "done")
166
201
  return { kind: "unchanged" };
167
202
  if (current.state === "drop") {
168
- return { kind: "refused", refusal: { kind: "invalid-lifecycle-transition", taskId: id, state: current.state, verb: "done" } };
203
+ return {
204
+ kind: "refused",
205
+ refusal: { kind: "invalid-lifecycle-transition", taskId: id, state: current.state, verb: "done" },
206
+ };
169
207
  }
170
208
  const at = currentTimestamp();
171
209
  const next = {
@@ -173,7 +211,11 @@ export async function settleTask(world, id) {
173
211
  state: "done",
174
212
  updatedAt: advanceTaskTimestamp(current.updatedAt, at),
175
213
  };
176
- const replaced = await replaceAuthority({ path: authorityPath(world, id), expected: snapshot.bytes.get(id), next: serializeTaskDocument(next) });
214
+ const replaced = await replaceAuthority({
215
+ path: authorityPath(world, id),
216
+ expected: snapshot.bytes.get(id),
217
+ next: serializeTaskDocument(next),
218
+ });
177
219
  return replaced === "replaced"
178
220
  ? { kind: "changed", task: taskView(next), action: "done" }
179
221
  : { kind: "retry", reason: "concurrent-modification" };
@@ -181,12 +223,15 @@ export async function settleTask(world, id) {
181
223
  return result === "busy" ? { kind: "retry", reason: "busy" } : result;
182
224
  }
183
225
  function readScope(namespace, scope) {
184
- return scope === "world" ? null : namespace ?? [];
226
+ return scope === "world" ? null : (namespace ?? []);
185
227
  }
186
228
  export async function listTasks(world, namespace, selection, scope, limit = DEFAULT_TASK_LIMIT) {
187
229
  const selected = readScope(namespace, scope);
188
230
  const board = (await readBoard(world)).board;
189
- return { kind: "accepted", value: projectPage(projectRows(board, createTaskRelations(board), selected, selection), limit) };
231
+ return {
232
+ kind: "accepted",
233
+ value: projectPage(projectRows(board, createTaskRelations(board), selected, selection), limit),
234
+ };
190
235
  }
191
236
  export async function readTaskDetails(world, ids) {
192
237
  const board = (await readBoard(world)).board;
@@ -197,6 +242,12 @@ export async function readTaskDetails(world, ids) {
197
242
  ? { kind: "accepted", value: details }
198
243
  : { kind: "refused", refusal: { kind: "task-missing", taskId: ids[missing] } };
199
244
  }
245
+ export async function observeTaskDetails(world, ids) {
246
+ const observed = await readTaskDetails(world, ids);
247
+ if (observed.kind !== "accepted")
248
+ return observed;
249
+ return { kind: "accepted", value: observed.value.map((facts) => ({ ...facts, task: taskView(facts.task) })) };
250
+ }
200
251
  export async function readyTasks(world, namespace, scope, parent, limit = DEFAULT_TASK_LIMIT) {
201
252
  const selected = readScope(namespace, scope);
202
253
  const board = (await readBoard(world)).board;
@@ -219,7 +270,10 @@ export async function blockedTasks(world, namespace, scope, parent, limit = DEFA
219
270
  if (parent !== undefined && !board.tasks.has(parent))
220
271
  return { kind: "refused", refusal: { kind: "task-missing", taskId: parent } };
221
272
  const relations = createTaskRelations(board);
222
- const blocked = { kind: "predicate", predicate: { field: "blocked", operator: "=", value: true } };
273
+ const blocked = {
274
+ kind: "predicate",
275
+ predicate: { field: "blocked", operator: "=", value: true },
276
+ };
223
277
  const expression = parent === undefined ? blocked : { kind: "and", terms: [blocked, underExpression(parent)] };
224
278
  const selectedRows = projectQuery(board, relations, {
225
279
  scope: selected,
@@ -8,7 +8,9 @@ function isTaskRelationPredicateField(value) {
8
8
  }
9
9
  const states = ["open", "in_progress", "on_hold", "done", "drop"];
10
10
  const comparisons = ["=", "!=", "<", "<=", ">", ">=", "~"];
11
- function fail(message) { throw new TypeError(message); }
11
+ function fail(message) {
12
+ throw new TypeError(message);
13
+ }
12
14
  function object(value, label) {
13
15
  if (typeof value !== "object" || value === null || Array.isArray(value))
14
16
  fail(`${label} must be an object`);
@@ -38,7 +40,9 @@ function comparison(value) {
38
40
  return value;
39
41
  }
40
42
  function timestamp(value, field) {
41
- if (typeof value !== "string" || !Number.isFinite(Date.parse(value)) || new Date(Date.parse(value)).toISOString() !== value) {
43
+ if (typeof value !== "string" ||
44
+ !Number.isFinite(Date.parse(value)) ||
45
+ new Date(Date.parse(value)).toISOString() !== value) {
42
46
  fail(`${field} must be a canonical UTC ISO timestamp`);
43
47
  }
44
48
  return value;
@@ -96,15 +100,24 @@ function predicate(value) {
96
100
  fail("query predicate field is required");
97
101
  const operator = comparison(input.operator);
98
102
  switch (field) {
99
- case "state": return normalizeStatePredicate(input, operator);
100
- case "priority": return normalizePriorityPredicate(input, operator);
101
- case "title": return normalizeTextPredicate(input, "title", operator);
102
- case "id": return normalizeTextPredicate(input, "id", operator);
103
- case "parent": return normalizeParentPredicate(input, operator);
104
- case "ready": return normalizeBooleanPredicate(input, "ready", operator);
105
- case "blocked": return normalizeBooleanPredicate(input, "blocked", operator);
106
- case "created": return normalizeTimestampPredicate(input, "created", operator);
107
- case "updated": return normalizeTimestampPredicate(input, "updated", operator);
103
+ case "state":
104
+ return normalizeStatePredicate(input, operator);
105
+ case "priority":
106
+ return normalizePriorityPredicate(input, operator);
107
+ case "title":
108
+ return normalizeTextPredicate(input, "title", operator);
109
+ case "id":
110
+ return normalizeTextPredicate(input, "id", operator);
111
+ case "parent":
112
+ return normalizeParentPredicate(input, operator);
113
+ case "ready":
114
+ return normalizeBooleanPredicate(input, "ready", operator);
115
+ case "blocked":
116
+ return normalizeBooleanPredicate(input, "blocked", operator);
117
+ case "created":
118
+ return normalizeTimestampPredicate(input, "created", operator);
119
+ case "updated":
120
+ return normalizeTimestampPredicate(input, "updated", operator);
108
121
  default:
109
122
  if (isTaskRelationPredicateField(field))
110
123
  return normalizeRelationPredicate(input, field, operator);
@@ -142,7 +155,9 @@ function compare(left, operator, right) {
142
155
  return left > right;
143
156
  return left >= right;
144
157
  }
145
- function membership(left, operator, right) { return operator === "=" ? left === right : left !== right; }
158
+ function membership(left, operator, right) {
159
+ return operator === "=" ? left === right : left !== right;
160
+ }
146
161
  function descendants(relations, parent) {
147
162
  const result = new Set(), pending = [parent];
148
163
  while (pending.length > 0) {
@@ -170,18 +185,30 @@ function matchRelation(selected, operator) {
170
185
  }
171
186
  function matchesPredicate(board, relations, task, value, under) {
172
187
  switch (value.field) {
173
- case "state": return compare(task.state, value.operator, value.value);
174
- case "priority": return compare(task.priority, value.operator, value.value);
175
- case "title": return matchText(task.title, value.operator, value.value);
176
- case "id": return matchText(task.id, value.operator, value.value);
177
- case "parent": return compare(task.parent ?? "", value.operator, value.value ?? "");
178
- case "under": return matchRelation(under.get(value.value)?.has(task.id) === true, value.operator);
179
- case "needs": return matchRelation(relation(board, relations, task.id, "needs", value.value), value.operator);
180
- case "blocks": return matchRelation(relation(board, relations, task.id, "blocks", value.value), value.operator);
181
- case "ready": return membership(taskDisposition(board, task) === "ready", value.operator, value.value);
182
- case "blocked": return membership(taskBlocked(board, task), value.operator, value.value);
183
- case "created": return compare(task.createdAt, value.operator, value.value);
184
- case "updated": return compare(task.updatedAt, value.operator, value.value);
188
+ case "state":
189
+ return compare(task.state, value.operator, value.value);
190
+ case "priority":
191
+ return compare(task.priority, value.operator, value.value);
192
+ case "title":
193
+ return matchText(task.title, value.operator, value.value);
194
+ case "id":
195
+ return matchText(task.id, value.operator, value.value);
196
+ case "parent":
197
+ return compare(task.parent ?? "", value.operator, value.value ?? "");
198
+ case "under":
199
+ return matchRelation(under.get(value.value)?.has(task.id) === true, value.operator);
200
+ case "needs":
201
+ return matchRelation(relation(board, relations, task.id, "needs", value.value), value.operator);
202
+ case "blocks":
203
+ return matchRelation(relation(board, relations, task.id, "blocks", value.value), value.operator);
204
+ case "ready":
205
+ return membership(taskDisposition(board, task) === "ready", value.operator, value.value);
206
+ case "blocked":
207
+ return membership(taskBlocked(board, task), value.operator, value.value);
208
+ case "created":
209
+ return compare(task.createdAt, value.operator, value.value);
210
+ case "updated":
211
+ return compare(task.updatedAt, value.operator, value.value);
185
212
  }
186
213
  }
187
214
  function matches(board, relations, task, expression, under) {
@@ -200,7 +227,8 @@ function row(board, relations, task) {
200
227
  parent: task.parent,
201
228
  needs: task.needs.map((need) => taskRef(board, need)),
202
229
  blocks: relations.blocks(task.id),
203
- createdAt: task.createdAt, updatedAt: task.updatedAt,
230
+ createdAt: task.createdAt,
231
+ updatedAt: task.updatedAt,
204
232
  };
205
233
  }
206
234
  function sortTasks(tasks, sort) {
@@ -217,7 +245,8 @@ function sortTasks(tasks, sort) {
217
245
  export function projectQuery(board, relations, input) {
218
246
  const { scope, expression, sort = "priority", limit = DEFAULT_TASK_LIMIT } = input;
219
247
  const under = new Map(queryUnderTargets(expression).map((target) => [target, descendants(relations, target)]));
220
- const selected = sortTasks([...board.tasks.values()].filter((task) => scope === null || sameNamespace(parseTaskId(task.id).namespace, scope))
248
+ const selected = sortTasks([...board.tasks.values()]
249
+ .filter((task) => scope === null || sameNamespace(parseTaskId(task.id).namespace, scope))
221
250
  .filter((task) => matches(board, relations, task, expression, under)), sort);
222
251
  const rows = selected.slice(0, limit).map((task) => row(board, relations, task));
223
252
  return { rows, total: selected.length, returned: rows.length, truncated: rows.length < selected.length };
@@ -12,7 +12,9 @@ export declare function replaceAuthority(input: Readonly<{
12
12
  expected: Uint8Array | null;
13
13
  next: Uint8Array;
14
14
  }>): Promise<"replaced" | "concurrent-modification">;
15
- export declare function nukeTaskAuthority(world: WorldRoot): Promise<void | "busy">;
15
+ export declare function nukeTaskAuthority(world: WorldRoot, options?: Readonly<{
16
+ timeoutMs?: number;
17
+ }>): Promise<void | "busy">;
16
18
  export declare function withTaskLocks<T>(input: Readonly<{
17
19
  world: WorldRoot;
18
20
  allocation: boolean;
@@ -1,8 +1,8 @@
1
1
  import { randomBytes } from "node:crypto";
2
- import { closeSync, fsyncSync, openSync, renameSync, writeFileSync, } from "node:fs";
3
- import { lstat, mkdir, readFile, readdir, unlink } from "node:fs/promises";
2
+ import { closeSync, fsyncSync, openSync, renameSync, writeFileSync } from "node:fs";
3
+ import { lstat, mkdir, readFile, readdir, rmdir, unlink } from "node:fs/promises";
4
4
  import { dirname, relative, resolve, sep } from "node:path";
5
- import { acquireSqliteTransactionLock, SqliteTransactionLockError } from "../coordination/sqlite-transaction-lock.js";
5
+ import { acquireSqliteTransactionLock, SqliteTransactionLockError, } from "../coordination/sqlite-transaction-lock.js";
6
6
  import { parseTaskDocument } from "./document.js";
7
7
  import { formatTaskId, parseTaskId, taskAuthorityPath } from "./identity.js";
8
8
  export const DEFAULT_TASK_LOCK_TIMEOUT_MS = 3_000;
@@ -17,8 +17,12 @@ function syncTaskDirectory(path) {
17
17
  closeSync(directory);
18
18
  }
19
19
  }
20
- function tasksDirectory(world) { return resolve(world, ".keiyaku", "tasks"); }
21
- function allocationLockPath(world) { return resolve(world, ".keiyaku", "locks", "task-allocation.sqlite"); }
20
+ function tasksDirectory(world) {
21
+ return resolve(world, ".keiyaku", "tasks");
22
+ }
23
+ function allocationLockPath(world) {
24
+ return resolve(world, ".keiyaku", "locks", "task-allocation.sqlite");
25
+ }
22
26
  async function authorityFiles(directory) {
23
27
  const files = [];
24
28
  let entries;
@@ -33,7 +37,7 @@ async function authorityFiles(directory) {
33
37
  for (const entry of entries) {
34
38
  const path = resolve(directory, entry.name);
35
39
  if (entry.isDirectory())
36
- files.push(...await authorityFiles(path));
40
+ files.push(...(await authorityFiles(path)));
37
41
  else if (entry.isFile() && entry.name.endsWith(".md"))
38
42
  files.push(path);
39
43
  }
@@ -45,6 +49,48 @@ function coordinateFromPath(tasksDirectory, path) {
45
49
  throw new Error(`invalid Task authority path: ${path}`);
46
50
  return parseTaskId(`task/${local.slice(0, -3).split(sep).join("/")}`);
47
51
  }
52
+ function ownedTaskId(tasksDirectory, path) {
53
+ try {
54
+ return formatTaskId(coordinateFromPath(tasksDirectory, path));
55
+ }
56
+ catch {
57
+ return null;
58
+ }
59
+ }
60
+ async function ownedAuthorityCandidates(directory) {
61
+ const candidates = [];
62
+ for (const path of await authorityFiles(directory)) {
63
+ const id = ownedTaskId(directory, path);
64
+ if (id !== null)
65
+ candidates.push({ id, path });
66
+ }
67
+ return candidates;
68
+ }
69
+ async function removeEmptyTaskDirectories(directory, removeSelf) {
70
+ let entries;
71
+ try {
72
+ entries = await readdir(directory, { withFileTypes: true });
73
+ }
74
+ catch (error) {
75
+ if (error.code === "ENOENT")
76
+ return;
77
+ throw error;
78
+ }
79
+ for (const entry of entries) {
80
+ if (entry.isDirectory())
81
+ await removeEmptyTaskDirectories(resolve(directory, entry.name), true);
82
+ }
83
+ if (!removeSelf)
84
+ return;
85
+ try {
86
+ await rmdir(directory);
87
+ }
88
+ catch (error) {
89
+ const code = error.code;
90
+ if (code !== "ENOENT" && code !== "ENOTEMPTY")
91
+ throw error;
92
+ }
93
+ }
48
94
  export async function readBoard(world) {
49
95
  const directory = tasksDirectory(world);
50
96
  const tasks = new Map();
@@ -98,21 +144,35 @@ export async function replaceAuthority(input) {
98
144
  try {
99
145
  await unlink(temporary);
100
146
  }
101
- catch { /* renamed or best effort */ }
147
+ catch {
148
+ /* renamed or best effort */
149
+ }
102
150
  }
103
151
  }
104
152
  function lockPath(world, id) {
105
153
  const coordinate = parseTaskId(id);
106
154
  return resolve(world, ".keiyaku", "locks", "task", ...coordinate.namespace, `${coordinate.localId}.sqlite`);
107
155
  }
108
- export async function nukeTaskAuthority(world) {
109
- const ids = [...(await readBoard(world)).board.tasks.keys()]
110
- .sort((left, right) => Buffer.compare(Buffer.from(left), Buffer.from(right)));
111
- if (ids.length === 0)
112
- return;
113
- const result = await withTaskLocks({ world, allocation: true, ids }, async () => {
114
- for (const id of ids)
115
- await unlink(authorityPath(world, id));
156
+ export async function nukeTaskAuthority(world, options) {
157
+ const directory = tasksDirectory(world);
158
+ const lockOptions = options?.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs };
159
+ const result = await withTaskLocks({ world, allocation: true, ids: [], ...lockOptions }, async () => {
160
+ const candidates = await ownedAuthorityCandidates(directory);
161
+ const ids = [...new Set(candidates.map((candidate) => candidate.id))].sort((left, right) => Buffer.compare(Buffer.from(left), Buffer.from(right)));
162
+ return await withTaskLocks({ world, allocation: false, ids, ...lockOptions }, async () => {
163
+ for (const candidate of candidates) {
164
+ try {
165
+ const stat = await lstat(candidate.path);
166
+ if (stat.isFile() && !stat.isSymbolicLink())
167
+ await unlink(candidate.path);
168
+ }
169
+ catch (error) {
170
+ if (error.code !== "ENOENT")
171
+ throw error;
172
+ }
173
+ }
174
+ await removeEmptyTaskDirectories(directory, false);
175
+ });
116
176
  });
117
177
  if (result === "busy")
118
178
  return "busy";
@@ -120,7 +180,9 @@ export async function nukeTaskAuthority(world) {
120
180
  export async function withTaskLocks(input, action) {
121
181
  const paths = [
122
182
  ...(input.allocation ? [allocationLockPath(input.world)] : []),
123
- ...[...new Set(input.ids)].sort((a, b) => Buffer.compare(Buffer.from(a), Buffer.from(b))).map((id) => lockPath(input.world, id)),
183
+ ...[...new Set(input.ids)]
184
+ .sort((a, b) => Buffer.compare(Buffer.from(a), Buffer.from(b)))
185
+ .map((id) => lockPath(input.world, id)),
124
186
  ];
125
187
  const held = [];
126
188
  try {
@@ -143,4 +205,6 @@ export async function withTaskLocks(input, action) {
143
205
  lock.close();
144
206
  }
145
207
  }
146
- export function authorityPath(world, id) { return taskAuthorityPath(tasksDirectory(world), parseTaskId(id)); }
208
+ export function authorityPath(world, id) {
209
+ return taskAuthorityPath(tasksDirectory(world), parseTaskId(id));
210
+ }
@@ -63,7 +63,9 @@ export async function executeVerification(input) {
63
63
  scratch = await input.materializeScratchCandidate(input.repository, input.candidate);
64
64
  }
65
65
  catch (error) {
66
- return { outcome: { kind: "candidate-unavailable", diagnostic: error instanceof Error ? error.message : String(error) } };
66
+ return {
67
+ outcome: { kind: "candidate-unavailable", diagnostic: error instanceof Error ? error.message : String(error) },
68
+ };
67
69
  }
68
70
  let outcome;
69
71
  let cleanup;
@@ -74,16 +76,17 @@ export async function executeVerification(input) {
74
76
  const hooks = worktreeHooksFrom({ settings: await input.projectSettings(scratch.cwd) });
75
77
  destroy = hooks.destroy;
76
78
  const readiness = await runHookCommands(scratch.cwd, hooks.create, input.signal);
77
- outcome = readiness.kind === "cancelled"
78
- ? { kind: "cancelled" }
79
- : readiness.kind === "failed"
80
- ? { kind: "environment-failure", command: readiness.command, detail: readiness.failure }
81
- : await executeDeclarations({
82
- declarations: input.declarations,
83
- cwd: scratch.cwd,
84
- environment: input.environment,
85
- ...(input.signal === undefined ? {} : { signal: input.signal }),
86
- });
79
+ outcome =
80
+ readiness.kind === "cancelled"
81
+ ? { kind: "cancelled" }
82
+ : readiness.kind === "failed"
83
+ ? { kind: "environment-failure", command: readiness.command, detail: readiness.failure }
84
+ : await executeDeclarations({
85
+ declarations: input.declarations,
86
+ cwd: scratch.cwd,
87
+ environment: input.environment,
88
+ ...(input.signal === undefined ? {} : { signal: input.signal }),
89
+ });
87
90
  }
88
91
  catch (error) {
89
92
  outcome = { kind: "environment-failure", diagnostic: error instanceof Error ? error.message : String(error) };
@@ -1,3 +1,4 @@
1
+ import { createHash } from "node:crypto";
1
2
  import { lstat, mkdir, readFile, unlink } from "node:fs/promises";
2
3
  import { dirname, join } from "node:path";
3
4
  import { replaceFileDurably } from "./coordination/durable-file.js";
@@ -312,7 +313,7 @@ async function mutatePlaceRegister(repository, mutate) {
312
313
  const next = mutate(current);
313
314
  const bytes = canonicalPlaceRegister(next);
314
315
  try {
315
- if (await readFile(location.authority, "utf8") === bytes)
316
+ if ((await readFile(location.authority, "utf8")) === bytes)
316
317
  return next;
317
318
  }
318
319
  catch (error) {
@@ -331,20 +332,31 @@ function withAppointments(register, contracts) {
331
332
  const additions = [];
332
333
  const occupied = new Set(register.byPlace.keys());
333
334
  const known = new Set(register.byContract.keys());
334
- let cursor;
335
335
  for (const contract of contracts) {
336
336
  if (known.has(contract))
337
337
  continue;
338
- let candidate = cursor === undefined ? nextPlace() : nextPlace(cursor);
339
- while (occupied.has(candidate))
340
- candidate = nextPlace(candidate);
338
+ const candidate = firstUnoccupiedPlace(contract, occupied);
341
339
  additions.push({ place: candidate, contract });
342
340
  occupied.add(candidate);
343
341
  known.add(contract);
344
- cursor = candidate;
345
342
  }
346
343
  return additions.length === 0 ? register : indexedRegister([...register.appointments, ...additions]);
347
344
  }
345
+ function stableStartIndex(contract) {
346
+ const digest = createHash("sha256").update(contract, "utf8").digest("hex");
347
+ return Number(BigInt(`0x${digest}`) % BigInt(CONTRACT_PLACES.length));
348
+ }
349
+ function firstUnoccupiedPlace(contract, occupied) {
350
+ const start = stableStartIndex(contract);
351
+ for (let generation = 1n;; generation += 1n) {
352
+ for (let offset = 0; offset < CONTRACT_PLACES.length; offset += 1) {
353
+ const base = CONTRACT_PLACES[(start + offset) % CONTRACT_PLACES.length];
354
+ const candidate = composePlace(base, generation);
355
+ if (!occupied.has(candidate))
356
+ return candidate;
357
+ }
358
+ }
359
+ }
348
360
  export async function appointManagedWorktrees(repository, contracts) {
349
361
  return await mutatePlaceRegister(repository, (current) => withAppointments(current, contracts));
350
362
  }
@@ -385,7 +397,7 @@ export async function nukeEmptyPlaceAuthority(repository) {
385
397
  }
386
398
  export async function readManagedWorktreeAppointment(repository, contract, register) {
387
399
  try {
388
- const snapshot = register ?? await readPlaceRegister(repository);
400
+ const snapshot = register ?? (await readPlaceRegister(repository));
389
401
  const appointed = appointmentFor(snapshot, contract);
390
402
  return appointed === undefined
391
403
  ? { kind: "unappointed" }
@@ -39,10 +39,14 @@ async function directory(input, label) {
39
39
  }
40
40
  return real;
41
41
  }
42
- function brand(path) { return path; }
43
- function marker(root) { return join(root, ".keiyaku"); }
42
+ function brand(path) {
43
+ return path;
44
+ }
45
+ function marker(root) {
46
+ return join(root, ".keiyaku");
47
+ }
44
48
  async function rejectReservedRoot(root) {
45
- if (root === await homeRoot()) {
49
+ if (root === (await homeRoot())) {
46
50
  throw new WorldError("home-world", "the user home directory cannot be a Keiyaku world");
47
51
  }
48
52
  if (root === parse(root).root) {
@@ -130,7 +134,13 @@ async function resolved(input) {
130
134
  });
131
135
  }
132
136
  export const World = Object.freeze({
133
- resolve(input) { return resolved(input); },
134
- async locate(input) { return (await resolved(input)).root; },
135
- at(input) { return exact(input); },
137
+ resolve(input) {
138
+ return resolved(input);
139
+ },
140
+ async locate(input) {
141
+ return (await resolved(input)).root;
142
+ },
143
+ at(input) {
144
+ return exact(input);
145
+ },
136
146
  });