@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,34 +1,58 @@
1
1
  import { chmod, lstat, readFile, unlink } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
3
  import { acquireSqliteTransactionLock } from "./coordination/sqlite-transaction-lock.js";
4
- import { createFileDurablyExclusive, repairDerivedFile, } from "./coordination/durable-file.js";
4
+ import { repairDerivedFile } from "./coordination/durable-file.js";
5
5
  import { contractId } from "./core/facts/types.js";
6
- import { registeredWorktrees, worktreeGitDirectory, worktreeRoot, } from "./git/repository.js";
6
+ import { worktreeGitDirectory, worktreeRoot } from "./git/repository.js";
7
7
  import { GitPlumbingError, runGit } from "./git/process.js";
8
8
  import { worktreePath } from "./git/workspace.js";
9
- import { appointmentFor, placeRegisterPath, } from "./workspace-place.js";
9
+ import { appointmentFor, placeRegisterPath } from "./workspace-place.js";
10
10
  const IGNORE_BYTES = ".gitignore\nKEIYAKU.md\n";
11
+ const PRIMARY_IGNORE_BYTES = "*\n!settings.json\n!tasks/\n!tasks/**\n";
11
12
  const APPOINTMENT_DESCRIPTION = "This is a read-only projection. Do not edit manually.";
12
- function appointmentBytes(contract) { return `---\ncontract: ${contract}\ndescription: ${APPOINTMENT_DESCRIPTION}\n---\n`; }
13
+ function appointmentBytes(contract) {
14
+ return `---\ncontract: ${contract}\ndescription: ${APPOINTMENT_DESCRIPTION}\n---\n`;
15
+ }
13
16
  function renderArc(arc) {
14
17
  return [
15
- "## Arc", "", "### Sequence", "", String(arc.seq), "", "### Title", "", arc.title.trimEnd(), "",
16
- "### Objective", "", arc.objective.trimEnd(), "", "### Brief", "", arc.brief.trimEnd(),
18
+ "## Arc",
19
+ "",
20
+ "### Sequence",
21
+ "",
22
+ String(arc.seq),
23
+ "",
24
+ "### Title",
25
+ "",
26
+ arc.title.trimEnd(),
27
+ "",
28
+ "### Objective",
29
+ "",
30
+ arc.objective.trimEnd(),
31
+ "",
32
+ "### Brief",
33
+ "",
34
+ arc.brief.trimEnd(),
17
35
  ].join("\n");
18
36
  }
19
37
  const FULFILLMENT = [
20
- "## Fulfillment", "", "### Appointment", "",
38
+ "## Fulfillment",
39
+ "",
40
+ "### Appointment",
41
+ "",
21
42
  "Each commission names exactly one seat: Deliverer or Reviewer.",
22
- "If no seat was named, stop and ask the caller. Never infer it.", "",
23
- "### Worktree", "",
43
+ "If no seat was named, stop and ask the caller. Never infer it.",
44
+ "",
45
+ "### Worktree",
46
+ "",
24
47
  "This file is a derived view of the journal-authoritative Contract. Never edit it to change the Contract.",
25
48
  "Treat the directory containing `.keiyaku/KEIYAKU.md` as the Contract worktree root.",
26
- "Read the complete Contract before acting and keep work inside that worktree.", "",
27
- "### Deliverer", "",
49
+ "Read the complete Contract before acting and keep work inside that worktree.",
50
+ "",
51
+ "### Deliverer",
52
+ "",
28
53
  "Implement and verify the Objective under the Design, Region, and Criteria in this Contract.",
29
54
  "When an Arc is active, stay within that current chapter.",
30
- "For work requiring three or more steps, prefer `keiyaku task -C <worktree>` "
31
- + "to organize and manage Tasks.",
55
+ "For work requiring three or more steps, prefer `keiyaku task -C <worktree>` " + "to organize and manage Tasks.",
32
56
  "Promptly update progress for Tasks already present in the current worktree.",
33
57
  "Leave lifecycle decisions to the caller; report the candidate, verification performed, and any unmet term.",
34
58
  "Deliver from this worktree. A clean worktree delivers HEAD; uncommitted work",
@@ -38,7 +62,8 @@ const FULFILLMENT = [
38
62
  "and deliver again; while the merge stays uncommitted that continuation uses",
39
63
  "`--include-dirty`. Unresolved paths always refuse.",
40
64
  "",
41
- "### Reviewer", "",
65
+ "### Reviewer",
66
+ "",
42
67
  "Review this Contract worktree against the complete Contract.",
43
68
  "Review the complete current worktree snapshot, not a named candidate commit.",
44
69
  "Do not modify it; report covered Criteria, findings, and missing evidence.",
@@ -49,7 +74,9 @@ export function renderContractGuidance(state) {
49
74
  state.terms.document.bytes.trimEnd(),
50
75
  ...(state.currentArc === undefined ? [] : [renderArc(state.currentArc.data)]),
51
76
  FULFILLMENT,
52
- ].join("\n\n").concat("\n");
77
+ ]
78
+ .join("\n\n")
79
+ .concat("\n");
53
80
  }
54
81
  async function isTracked(repository, relativePath) {
55
82
  try {
@@ -88,57 +115,6 @@ export async function readContractAppointment(repository) {
88
115
  const contract = appointedContract(await readFile(path, "utf8"));
89
116
  return contract === undefined ? { kind: "invalid", path } : { kind: "appointed", path, contract };
90
117
  }
91
- async function readHereContractWorkspaceAppointment(repository, contract) {
92
- const matches = [];
93
- let worktrees;
94
- try {
95
- worktrees = await registeredWorktrees(repository);
96
- }
97
- catch (error) {
98
- if (error instanceof GitPlumbingError)
99
- return { kind: "failed", diagnostic: error.message };
100
- throw error;
101
- }
102
- for (const worktree of worktrees) {
103
- const appointment = await readContractAppointment({ ...repository, effectiveCwd: worktree.path });
104
- if (appointment.kind === "appointed" && appointment.contract === contract)
105
- matches.push(worktree.path);
106
- }
107
- if (matches.length === 0)
108
- return { kind: "unappointed" };
109
- if (matches.length > 1)
110
- return { kind: "conflicted", paths: matches.sort() };
111
- return { kind: "appointed", path: matches[0] };
112
- }
113
- function conflictDiagnostic(paths) {
114
- const displayed = paths.slice(0, 3);
115
- return `duplicate here Contract workspace appointments: ${displayed.join(", ")}${paths.length > displayed.length ? ", ..." : ""}`;
116
- }
117
- /** Fold existing here appointments over all registered worktrees for one Contract. */
118
- export async function resolveHereContractWorkspace(repository, contract) {
119
- const appointment = await readHereContractWorkspaceAppointment(repository, contract);
120
- if (appointment.kind === "failed")
121
- return { kind: "failed", diagnostic: appointment.diagnostic, cause: "observation" };
122
- if (appointment.kind === "conflicted") {
123
- return { kind: "failed", diagnostic: conflictDiagnostic(appointment.paths), cause: "duplicate" };
124
- }
125
- return appointment;
126
- }
127
- export async function reserveContractWorktree(repository, contract) {
128
- const worktree = await worktreeRoot(repository);
129
- const scoped = { ...repository, effectiveCwd: worktree };
130
- const ignore = generatedPath(worktree, ".gitignore");
131
- if (await isTracked(scoped, ".keiyaku/.gitignore"))
132
- throw new Error(`generated path is tracked by Git: ${ignore}`);
133
- await repairDerivedFile(ignore, IGNORE_BYTES);
134
- const path = generatedPath(worktree, "KEIYAKU.md");
135
- if (await isTracked(scoped, ".keiyaku/KEIYAKU.md"))
136
- throw new Error(`generated path is tracked by Git: ${path}`);
137
- if (await createFileDurablyExclusive(path, appointmentBytes(contract), 0o444))
138
- return { kind: "reserved", path };
139
- const appointment = await readContractAppointment(repository);
140
- return appointment.kind === "absent" ? { kind: "invalid", path } : appointment;
141
- }
142
118
  export async function withContractWorktreeAppointment(repository, action) {
143
119
  const root = await worktreeRoot(repository);
144
120
  const lock = await acquireSqliteTransactionLock({
@@ -156,39 +132,13 @@ export async function releaseContractWorktree(repository, contract) {
156
132
  const appointment = await readContractAppointment(repository);
157
133
  if (appointment.kind !== "appointed" || appointment.contract !== contract)
158
134
  return;
159
- if (await readFile(appointment.path, "utf8") !== appointmentBytes(contract))
135
+ if ((await readFile(appointment.path, "utf8")) !== appointmentBytes(contract))
160
136
  return;
161
137
  try {
162
138
  await unlink(appointment.path);
163
139
  }
164
- catch { /* reservation cleanup is best effort */ }
165
- }
166
- async function removeOwnedHereSupport(worktree) {
167
- const path = generatedPath(worktree, ".gitignore");
168
- try {
169
- const value = await lstat(path);
170
- if (!value.isFile() || value.isSymbolicLink())
171
- return 0;
172
- if (await readFile(path, "utf8") !== IGNORE_BYTES)
173
- return 0;
174
- await unlink(path);
175
- return 1;
176
- }
177
- catch (error) {
178
- if (error.code === "ENOENT")
179
- return 0;
180
- throw error;
181
- }
182
- }
183
- /** Remove matching here appointments and their exact generated support from registered worktrees. */
184
- export async function nukeHereAppointments(repository) {
185
- for (const registered of await registeredWorktrees(repository)) {
186
- const scoped = { ...repository, effectiveCwd: registered.path };
187
- const appointment = await readContractAppointment(scoped);
188
- if (appointment.kind === "appointed") {
189
- await unlink(appointment.path);
190
- await removeOwnedHereSupport(registered.path);
191
- }
140
+ catch {
141
+ /* reservation cleanup is best effort */
192
142
  }
193
143
  }
194
144
  async function repair(repository, worktree, relativePath, bytes) {
@@ -200,11 +150,31 @@ async function repair(repository, worktree, relativePath, bytes) {
200
150
  try {
201
151
  await chmod(path, 0o444);
202
152
  }
203
- catch { /* advisory protection only */ }
153
+ catch {
154
+ /* advisory protection only */
155
+ }
204
156
  }
205
157
  return { kind: "contract-file", path, action };
206
158
  }
207
159
  async function materialize(repository, worktree, guidance) {
160
+ const effects = [];
161
+ const failed = (target, path, error) => ({
162
+ effects,
163
+ lag: [
164
+ {
165
+ kind: "contract-file-failed",
166
+ worktree: target,
167
+ path,
168
+ diagnostic: error instanceof Error ? error.message : String(error),
169
+ },
170
+ ],
171
+ });
172
+ try {
173
+ effects.push(await repair({ ...repository, effectiveCwd: repository.primaryWorktree }, repository.primaryWorktree, ".keiyaku/.gitignore", PRIMARY_IGNORE_BYTES));
174
+ }
175
+ catch (error) {
176
+ return failed(repository.primaryWorktree, join(repository.primaryWorktree, ".keiyaku", ".gitignore"), error);
177
+ }
208
178
  const stat = await lstat(worktree).catch((error) => {
209
179
  if (error.code === "ENOENT")
210
180
  return undefined;
@@ -212,17 +182,18 @@ async function materialize(repository, worktree, guidance) {
212
182
  });
213
183
  if (stat === undefined || !stat.isDirectory()) {
214
184
  return {
215
- effects: [],
216
- lag: [{
185
+ effects,
186
+ lag: [
187
+ {
217
188
  kind: "contract-file-failed",
218
189
  worktree,
219
190
  path: generatedPath(worktree, "KEIYAKU.md"),
220
191
  diagnostic: "Contract worktree is unavailable",
221
- }],
192
+ },
193
+ ],
222
194
  };
223
195
  }
224
196
  const scoped = { ...repository, effectiveCwd: worktree };
225
- const effects = [];
226
197
  for (const [relativePath, bytes] of [
227
198
  [".keiyaku/.gitignore", IGNORE_BYTES],
228
199
  [".keiyaku/KEIYAKU.md", guidance],
@@ -231,73 +202,26 @@ async function materialize(repository, worktree, guidance) {
231
202
  effects.push(await repair(scoped, worktree, relativePath, bytes));
232
203
  }
233
204
  catch (error) {
234
- return {
235
- effects,
236
- lag: [{
237
- kind: "contract-file-failed",
238
- worktree,
239
- path: join(worktree, relativePath),
240
- diagnostic: error instanceof Error ? error.message : String(error),
241
- }],
242
- };
205
+ return failed(worktree, join(worktree, relativePath), error);
243
206
  }
244
207
  }
245
208
  return { effects, lag: [] };
246
209
  }
247
- async function here(repository, state, guidance, onConflict) {
248
- const appointment = await resolveHereContractWorkspace(repository, state.id);
249
- if (appointment.kind === "appointed")
250
- return await materialize(repository, appointment.path, guidance);
251
- if (appointment.kind === "failed") {
252
- if (onConflict !== undefined)
253
- return onConflict(appointment.diagnostic);
254
- throw new Error(appointment.diagnostic);
255
- }
256
- return {
257
- effects: [],
258
- lag: [{
259
- kind: "contract-file-failed",
260
- worktree: repository.primaryWorktree,
261
- path: generatedPath(repository.primaryWorktree, "KEIYAKU.md"),
262
- diagnostic: "here Contract is unappointed",
263
- }],
264
- };
265
- }
266
- async function removeHere(repository, state) {
267
- const effects = [];
268
- for (const worktree of await registeredWorktrees(repository)) {
269
- const appointment = await readContractAppointment({ ...repository, effectiveCwd: worktree.path });
270
- if (appointment.kind !== "appointed" || appointment.contract !== state.id)
271
- continue;
272
- try {
273
- await unlink(appointment.path);
274
- effects.push({ kind: "contract-file", path: appointment.path, action: "removed" });
275
- }
276
- catch (error) {
277
- return { effects, lag: [{ kind: "contract-file-failed", worktree: worktree.path, path: appointment.path, diagnostic: error instanceof Error ? error.message : String(error) }] };
278
- }
279
- }
280
- return { effects, lag: [] };
281
- }
282
- export async function projectContractWorktree(repository, state, register, onConflict) {
283
- if (state === null || state.terminal) {
284
- return state?.coordinates.workspace === "here"
285
- ? await removeHere(repository, state)
286
- : { effects: [], lag: [] };
287
- }
288
- if (state.coordinates.workspace === "here") {
289
- return await here(repository, state, renderContractGuidance(state), onConflict);
290
- }
210
+ export async function projectContractWorktree(repository, state, register, _onConflict) {
211
+ if (state === null || state.terminal)
212
+ return { effects: [], lag: [] };
291
213
  const appointed = register === undefined ? undefined : appointmentFor(register, state.id);
292
214
  if (appointed === undefined) {
293
215
  return {
294
216
  effects: [],
295
- lag: [{
217
+ lag: [
218
+ {
296
219
  kind: "contract-file-failed",
297
220
  worktree: repository.primaryWorktree,
298
221
  path: placeRegisterPath(repository),
299
222
  diagnostic: "managed Contract is unappointed",
300
- }],
223
+ },
224
+ ],
301
225
  };
302
226
  }
303
227
  return await materialize(repository, worktreePath(repository, appointed.place), renderContractGuidance(state));
@@ -1,5 +1,5 @@
1
1
  import { randomBytes } from "node:crypto";
2
- import { closeSync, fsyncSync, openSync, renameSync, writeFileSync, } from "node:fs";
2
+ import { closeSync, fsyncSync, openSync, renameSync, writeFileSync } from "node:fs";
3
3
  import { lstat, mkdir, readFile, unlink } from "node:fs/promises";
4
4
  import { dirname, resolve } from "node:path";
5
5
  export function syncDirectory(path) {
@@ -34,7 +34,9 @@ export async function replaceFileDurably(path, bytes) {
34
34
  try {
35
35
  await unlink(temporary);
36
36
  }
37
- catch { /* renamed, absent, or best-effort cleanup */ }
37
+ catch {
38
+ /* renamed, absent, or best-effort cleanup */
39
+ }
38
40
  if (error.code !== "EEXIST")
39
41
  throw error;
40
42
  }
@@ -61,7 +63,9 @@ export async function createFileDurablyExclusive(path, bytes, mode = 0o600) {
61
63
  try {
62
64
  await unlink(path);
63
65
  }
64
- catch { /* absent or best-effort cleanup */ }
66
+ catch {
67
+ /* absent or best-effort cleanup */
68
+ }
65
69
  throw error;
66
70
  }
67
71
  }
@@ -21,9 +21,11 @@ function isBusy(error) {
21
21
  return false;
22
22
  const value = error;
23
23
  const message = String(value.message ?? "").toLowerCase();
24
- return value.errcode === SQLITE_BUSY || value.code === "ERR_SQLITE_BUSY"
25
- || value.code === "ERR_SQLITE_LOCKED" || message.includes("database is locked")
26
- || message.includes("database is busy");
24
+ return (value.errcode === SQLITE_BUSY ||
25
+ value.code === "ERR_SQLITE_BUSY" ||
26
+ value.code === "ERR_SQLITE_LOCKED" ||
27
+ message.includes("database is locked") ||
28
+ message.includes("database is busy"));
27
29
  }
28
30
  function closeDatabase(database) {
29
31
  let failure;
@@ -56,7 +58,9 @@ async function openLock(path, mode) {
56
58
  catch (error) {
57
59
  if (error instanceof SqliteTransactionLockError)
58
60
  throw error;
59
- throw new SqliteTransactionLockError(`cannot prepare SQLite lock: ${detail(error)}`, "open-failed", { cause: error });
61
+ throw new SqliteTransactionLockError(`cannot prepare SQLite lock: ${detail(error)}`, "open-failed", {
62
+ cause: error,
63
+ });
60
64
  }
61
65
  let database;
62
66
  try {
@@ -72,32 +76,45 @@ async function openLock(path, mode) {
72
76
  closeDatabase(database);
73
77
  if (isBusy(error))
74
78
  throw error;
75
- throw new SqliteTransactionLockError(`cannot acquire SQLite lock: ${detail(error)}`, "open-failed", { cause: error });
79
+ throw new SqliteTransactionLockError(`cannot acquire SQLite lock: ${detail(error)}`, "open-failed", {
80
+ cause: error,
81
+ });
76
82
  }
77
83
  let closed = false;
78
- return { close() {
84
+ return {
85
+ close() {
79
86
  if (closed)
80
87
  return;
81
88
  closed = true;
82
89
  const failure = closeDatabase(database);
83
90
  if (failure !== undefined) {
84
- throw new SqliteTransactionLockError(`cannot release SQLite lock: ${detail(failure)}`, "release-failed", { cause: failure });
91
+ throw new SqliteTransactionLockError(`cannot release SQLite lock: ${detail(failure)}`, "release-failed", {
92
+ cause: failure,
93
+ });
85
94
  }
86
- } };
95
+ },
96
+ };
87
97
  }
88
98
  function wait(milliseconds, signal) {
89
99
  return new Promise((resolve, reject) => {
90
- const finish = () => { signal?.removeEventListener("abort", abort); resolve(); };
100
+ const finish = () => {
101
+ signal?.removeEventListener("abort", abort);
102
+ resolve();
103
+ };
91
104
  const timer = setTimeout(finish, milliseconds);
92
- const abort = () => { clearTimeout(timer); signal?.removeEventListener("abort", abort); reject(signal?.reason); };
105
+ const abort = () => {
106
+ clearTimeout(timer);
107
+ signal?.removeEventListener("abort", abort);
108
+ reject(signal?.reason);
109
+ };
93
110
  signal?.addEventListener("abort", abort, { once: true });
94
111
  if (signal?.aborted)
95
112
  abort();
96
113
  });
97
114
  }
98
115
  export async function acquireSqliteTransactionLock(input) {
99
- if (input.path.length === 0 || (input.timeoutMs !== undefined
100
- && (!Number.isSafeInteger(input.timeoutMs) || input.timeoutMs < 0))) {
116
+ if (input.path.length === 0 ||
117
+ (input.timeoutMs !== undefined && (!Number.isSafeInteger(input.timeoutMs) || input.timeoutMs < 0))) {
101
118
  throw new SqliteTransactionLockError("SQLite lock path and timeout must be valid", "invalid");
102
119
  }
103
120
  const started = performance.now();
@@ -87,7 +87,8 @@ function validateTimestamp(value, path) {
87
87
  }
88
88
  const milliseconds = Date.parse(text);
89
89
  const canonical = Number.isFinite(milliseconds) ? new Date(milliseconds).toISOString() : "";
90
- if (!Number.isFinite(milliseconds) || (text.includes(".") ? canonical !== text : canonical.replace(".000Z", "Z") !== text)) {
90
+ if (!Number.isFinite(milliseconds) ||
91
+ (text.includes(".") ? canonical !== text : canonical.replace(".000Z", "Z") !== text)) {
91
92
  fail(path, "expected a real canonical UTC timestamp");
92
93
  }
93
94
  return text;
@@ -96,7 +97,7 @@ function validateCoordinates(value, path) {
96
97
  const object = requireRecord(value, path);
97
98
  requireKeys(object, ["start", "workspace"], path, ["target"]);
98
99
  const workspace = stringValue(object.workspace, `${path}.workspace`);
99
- if (workspace !== "worktree" && workspace !== "here")
100
+ if (workspace !== "worktree")
100
101
  fail(`${path}.workspace`, "unknown workspace");
101
102
  return {
102
103
  start: brandedValue(object.start, `${path}.start`, snapshotId),
@@ -282,5 +283,8 @@ export function decodeJournal(journal) {
282
283
  return [];
283
284
  if (journal.includes("\r") || !journal.endsWith("\n"))
284
285
  throw new AuthorityCorruptionError("journal must use LF lines and end with LF");
285
- return journal.slice(0, -1).split("\n").map((line) => decodeEntry(line));
286
+ return journal
287
+ .slice(0, -1)
288
+ .split("\n")
289
+ .map((line) => decodeEntry(line));
286
290
  }
@@ -56,7 +56,7 @@ export declare function gate(value: string): Gate;
56
56
  export type ContractCoordinates = Readonly<{
57
57
  start: SnapshotId;
58
58
  target?: string;
59
- workspace: "worktree" | "here";
59
+ workspace: "worktree";
60
60
  }>;
61
61
  export type ContractTerms = Readonly<{
62
62
  document: Readonly<{
@@ -12,11 +12,16 @@ function parseKey(value, index) {
12
12
  }
13
13
  try {
14
14
  switch (value[0]) {
15
- case "document": return { kind: "document", value: documentKey(value[1]) };
16
- case "segment": return { kind: "segment", value: documentSegmentKey(value[1]) };
17
- case "snapshot": return { kind: "snapshot", value: snapshotId(value[1]) };
18
- case "change": return { kind: "change", value: changeId(value[1]) };
19
- default: throw new AuthorityCorruptionError(`unknown dependency key kind '${value[0]}'`);
15
+ case "document":
16
+ return { kind: "document", value: documentKey(value[1]) };
17
+ case "segment":
18
+ return { kind: "segment", value: documentSegmentKey(value[1]) };
19
+ case "snapshot":
20
+ return { kind: "snapshot", value: snapshotId(value[1]) };
21
+ case "change":
22
+ return { kind: "change", value: changeId(value[1]) };
23
+ default:
24
+ throw new AuthorityCorruptionError(`unknown dependency key kind '${value[0]}'`);
20
25
  }
21
26
  }
22
27
  catch (error) {
@@ -69,10 +74,12 @@ function currentKeys(state) {
69
74
  return new Set([
70
75
  encodeKey({ kind: "document", value: state.terms.document.key }),
71
76
  ...state.terms.segments.map((value) => encodeKey({ kind: "segment", value })),
72
- ...(integration === undefined ? [] : [
73
- encodeKey({ kind: "snapshot", value: integration.snapshot }),
74
- encodeKey({ kind: "change", value: integration.changeId }),
75
- ]),
77
+ ...(integration === undefined
78
+ ? []
79
+ : [
80
+ encodeKey({ kind: "snapshot", value: integration.snapshot }),
81
+ encodeKey({ kind: "change", value: integration.changeId }),
82
+ ]),
76
83
  ]);
77
84
  }
78
85
  /** Build one memoized currentness test for a folded contract snapshot. */
@@ -10,4 +10,4 @@ export type AbandonRefusal = Readonly<{
10
10
  kind: "contract-missing" | "terminal";
11
11
  contractId: ContractId;
12
12
  }>;
13
- export declare function decideAbandon({ input, attempt, observation }: DecideInput<AbandonInput>): OfferDecision<AbandonRefusal>;
13
+ export declare function decideAbandon({ input, attempt, observation, }: DecideInput<AbandonInput>): OfferDecision<AbandonRefusal>;
@@ -1,5 +1,5 @@
1
1
  import { activeContract } from "../facts/observation.js";
2
- export function decideAbandon({ input, attempt, observation }) {
2
+ export function decideAbandon({ input, attempt, observation, }) {
3
3
  const id = input.contractId;
4
4
  const current = activeContract(observation, id);
5
5
  if ("kind" in current)
@@ -11,4 +11,4 @@ export type AmendRefusal = Readonly<{
11
11
  kind: "contract-missing" | "terminal" | "terms-moved" | "unknown-prerequisite" | "cyclic-prerequisite";
12
12
  contractId: ContractId;
13
13
  }>;
14
- export declare function decideAmend<Failure>({ input, attempt, observation }: DecideInput<AmendInput<Failure>>): OfferDecision<AmendRefusal | Failure>;
14
+ export declare function decideAmend<Failure>({ input, attempt, observation, }: DecideInput<AmendInput<Failure>>): OfferDecision<AmendRefusal | Failure>;
@@ -1,6 +1,6 @@
1
1
  import { prerequisitesReach, samePrerequisites } from "../facts/eligibility.js";
2
2
  import { activeContract, prerequisiteStatus } from "../facts/observation.js";
3
- export function decideAmend({ input, attempt, observation }) {
3
+ export function decideAmend({ input, attempt, observation, }) {
4
4
  const id = input.contractId;
5
5
  const current = activeContract(observation, id);
6
6
  if ("kind" in current)
@@ -9,12 +9,12 @@ export function decideAmend({ input, attempt, observation }) {
9
9
  throw new Error("existing contract requires an amend preparation and source terms");
10
10
  }
11
11
  const source = input.source;
12
- const termsCurrent = source.document.key === current.terms.document.key
13
- && source.segments.length === current.terms.segments.length
14
- && source.segments.every((value, index) => value === current.terms.segments[index])
15
- && source.gates.length === current.terms.gates.length
16
- && source.gates.every((value, index) => value === current.terms.gates[index])
17
- && samePrerequisites(source.after, current.terms.after);
12
+ const termsCurrent = source.document.key === current.terms.document.key &&
13
+ source.segments.length === current.terms.segments.length &&
14
+ source.segments.every((value, index) => value === current.terms.segments[index]) &&
15
+ source.gates.length === current.terms.gates.length &&
16
+ source.gates.every((value, index) => value === current.terms.gates[index]) &&
17
+ samePrerequisites(source.after, current.terms.after);
18
18
  if (!termsCurrent) {
19
19
  return { kind: "refused", refusal: { kind: "terms-moved", contractId: id } };
20
20
  }
@@ -17,5 +17,8 @@ export function decideAttestation({ input, attempt, observation, }) {
17
17
  ...(input.actor === undefined ? {} : { actor: input.actor }),
18
18
  data: input.preparation.data,
19
19
  };
20
- return { kind: "offer", offer: { facts: [{ contractId: input.contractId, expectedHead: state.head, entries: [attestation] }] } };
20
+ return {
21
+ kind: "offer",
22
+ offer: { facts: [{ contractId: input.contractId, expectedHead: state.head, entries: [attestation] }] },
23
+ };
21
24
  }
@@ -10,4 +10,4 @@ export type BindRefusal = Readonly<{
10
10
  kind: "contract-exists" | "invalid-after" | "unknown-prerequisite";
11
11
  contractId: ContractId;
12
12
  }>;
13
- export declare function decideBind<Failure>({ input, attempt, observation }: DecideInput<BindInput<Failure>>): OfferDecision<BindRefusal | Failure>;
13
+ export declare function decideBind<Failure>({ input, attempt, observation, }: DecideInput<BindInput<Failure>>): OfferDecision<BindRefusal | Failure>;
@@ -1,5 +1,5 @@
1
1
  import { contractState, prerequisiteStatus } from "../facts/observation.js";
2
- export function decideBind({ input, attempt, observation }) {
2
+ export function decideBind({ input, attempt, observation, }) {
3
3
  const id = input.contractId;
4
4
  const current = contractState(observation, id);
5
5
  if (current !== null)
@@ -17,5 +17,5 @@ export type PlacementRefusal = Readonly<{
17
17
  contractId: ContractId;
18
18
  unmet: readonly UnmetPrerequisite[];
19
19
  }>;
20
- export declare function decidePlacement({ input, attempt, observation }: DecideInput<PlacementInput>): OfferDecision<PlacementRefusal>;
20
+ export declare function decidePlacement({ input, attempt, observation, }: DecideInput<PlacementInput>): OfferDecision<PlacementRefusal>;
21
21
  export {};
@@ -16,7 +16,7 @@ function unmetPrerequisites(prerequisites, observation) {
16
16
  }
17
17
  return unmet;
18
18
  }
19
- export function decidePlacement({ input, attempt, observation }) {
19
+ export function decidePlacement({ input, attempt, observation, }) {
20
20
  const id = input.contractId;
21
21
  const current = activeContract(observation, id);
22
22
  if ("kind" in current)
@@ -46,13 +46,15 @@ export function decidePlacement({ input, attempt, observation }) {
46
46
  kind: "offer",
47
47
  offer: {
48
48
  facts: [{ contractId: id, expectedHead: current.head, entries: [claimed] }],
49
- ...(current.coordinates.target === undefined ? {} : {
50
- target: {
51
- target: current.coordinates.target,
52
- expectedOid: integration.predecessor,
53
- newOid: integration.snapshot,
54
- },
55
- }),
49
+ ...(current.coordinates.target === undefined
50
+ ? {}
51
+ : {
52
+ target: {
53
+ target: current.coordinates.target,
54
+ expectedOid: integration.predecessor,
55
+ newOid: integration.snapshot,
56
+ },
57
+ }),
56
58
  },
57
59
  };
58
60
  }