@astrosheep/keiyaku 4.5.7 → 4.5.8

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 (234) hide show
  1. package/README.md +10 -0
  2. package/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +1 -1
  3. package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +1 -1
  4. package/build/integrations/marketplace/plugins/keiyaku/opencode.js +3 -2
  5. package/build/integrations/marketplace/plugins/keiyaku/package.json +1 -1
  6. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +10 -9
  7. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +22 -1
  8. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-babysit/SKILL.md +35 -96
  9. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +28 -49
  10. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +21 -15
  11. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +148 -89
  12. package/build/src/akuma/abort.d.ts +2 -1
  13. package/build/src/akuma/abort.js +6 -23
  14. package/build/src/akuma/akuma-handle.js +4 -2
  15. package/build/src/akuma/akuma-observe.d.ts +2 -0
  16. package/build/src/akuma/akuma-observe.js +1 -0
  17. package/build/src/akuma/akuma-product.js +9 -7
  18. package/build/src/akuma/akuma.d.ts +372 -9
  19. package/build/src/akuma/akuma.js +31 -1
  20. package/build/src/akuma/allowed.d.ts +1 -1
  21. package/build/src/akuma/allowed.js +1 -0
  22. package/build/src/akuma/body.d.ts +6 -4
  23. package/build/src/akuma/body.js +5 -3
  24. package/build/src/akuma/call-request.d.ts +80 -0
  25. package/build/src/akuma/call-request.js +211 -0
  26. package/build/src/akuma/heart/facts.d.ts +6 -78
  27. package/build/src/akuma/heart/index.d.ts +1 -1
  28. package/build/src/akuma/heart/request-authority.d.ts +5 -4
  29. package/build/src/akuma/heart/request-authority.js +20 -34
  30. package/build/src/akuma/heart/request-rows.js +12 -23
  31. package/build/src/akuma/heart/schema.d.ts +1 -1
  32. package/build/src/akuma/heart/schema.js +5 -13
  33. package/build/src/akuma/index.d.ts +1 -1
  34. package/build/src/akuma/index.js +1 -1
  35. package/build/src/akuma/projection-read.d.ts +1 -0
  36. package/build/src/akuma/projection-read.js +8 -1
  37. package/build/src/akuma/projection.d.ts +856 -137
  38. package/build/src/akuma/projection.js +190 -0
  39. package/build/src/akuma/provider.d.ts +30 -3
  40. package/build/src/akuma/provider.js +104 -0
  41. package/build/src/akuma/providers/acp/core.d.ts +2 -2
  42. package/build/src/akuma/providers/acp/core.js +13 -6
  43. package/build/src/akuma/providers/acp/index.js +5 -4
  44. package/build/src/akuma/providers/claude/index.d.ts +3 -3
  45. package/build/src/akuma/providers/claude/index.js +16 -5
  46. package/build/src/akuma/providers/codex-app-server/events.js +1 -1
  47. package/build/src/akuma/providers/codex-app-server/index.js +18 -6
  48. package/build/src/akuma/providers/grok-build/index.js +5 -4
  49. package/build/src/akuma/providers/opencode-sdk/index.js +66 -25
  50. package/build/src/akuma/providers/opencode-sdk/session.d.ts +3 -2
  51. package/build/src/akuma/providers/opencode-sdk/session.js +28 -11
  52. package/build/src/akuma/providers/pi/index.js +42 -14
  53. package/build/src/akuma/request-lifecycle.d.ts +3 -9
  54. package/build/src/akuma/request-lifecycle.js +4 -4
  55. package/build/src/akuma/request-rendezvous.d.ts +26 -0
  56. package/build/src/akuma/request-rendezvous.js +100 -0
  57. package/build/src/akuma/request-serve.d.ts +5 -65
  58. package/build/src/akuma/request-serve.js +81 -208
  59. package/build/src/akuma/request-wire.d.ts +109 -115
  60. package/build/src/akuma/request-wire.js +63 -318
  61. package/build/src/akuma/requests.d.ts +19 -47
  62. package/build/src/akuma/requests.js +51 -107
  63. package/build/src/akuma/turn-drive.d.ts +5 -3
  64. package/build/src/akuma/turn-drive.js +32 -10
  65. package/build/src/akuma-body.d.ts +7 -2
  66. package/build/src/akuma-body.js +68 -46
  67. package/build/src/cli/accepted.d.ts +1 -1
  68. package/build/src/cli/accepted.js +2 -2
  69. package/build/src/cli/commands/akuma-invoke.d.ts +2 -0
  70. package/build/src/cli/commands/akuma-invoke.js +17 -9
  71. package/build/src/cli/commands/akuma.js +7 -7
  72. package/build/src/cli/commands/contract-invoke.d.ts +3 -2
  73. package/build/src/cli/commands/contract-invoke.js +26 -19
  74. package/build/src/cli/commands/contract.d.ts +13 -13
  75. package/build/src/cli/commands/contract.js +32 -17
  76. package/build/src/cli/commands/install.d.ts +1 -1
  77. package/build/src/cli/commands/install.js +1 -1
  78. package/build/src/cli/commands/task-invoke.d.ts +3 -0
  79. package/build/src/cli/commands/task-invoke.js +23 -140
  80. package/build/src/cli/commands/task.js +54 -20
  81. package/build/src/cli/invoke.js +39 -25
  82. package/build/src/cli/main.js +2 -1
  83. package/build/src/cli/parse.d.ts +2 -2
  84. package/build/src/cli/parse.js +37 -22
  85. package/build/src/cli/render/akuma-activity.d.ts +4 -1
  86. package/build/src/cli/render/akuma-activity.js +45 -30
  87. package/build/src/cli/render/akuma-tool.d.ts +5 -1
  88. package/build/src/cli/render/akuma.js +5 -9
  89. package/build/src/cli/render/audit.js +1 -1
  90. package/build/src/cli/render/catalog.js +80 -21
  91. package/build/src/cli/render/contract-observation.d.ts +2 -0
  92. package/build/src/cli/render/contract-observation.js +11 -0
  93. package/build/src/cli/render/contract.js +25 -65
  94. package/build/src/cli/render/kanshi-akuma.d.ts +8 -0
  95. package/build/src/cli/render/kanshi-akuma.js +111 -0
  96. package/build/src/cli/render/kanshi.js +108 -213
  97. package/build/src/cli/render/receipt.d.ts +1 -2
  98. package/build/src/cli/render/receipt.js +76 -26
  99. package/build/src/cli/render/refusal.js +2 -10
  100. package/build/src/cli/render/region.js +5 -2
  101. package/build/src/cli/render/settings.d.ts +1 -1
  102. package/build/src/cli/render/settings.js +27 -8
  103. package/build/src/cli/render/task.js +22 -5
  104. package/build/src/cli/render/terminal.d.ts +28 -0
  105. package/build/src/cli/render/terminal.js +65 -0
  106. package/build/src/cli/result.d.ts +3 -3
  107. package/build/src/cli/runtime.js +3 -1
  108. package/build/src/cli/selectors.d.ts +2 -1
  109. package/build/src/cli/selectors.js +4 -3
  110. package/build/src/cli/square-edge.js +120 -36
  111. package/build/src/cli/usage.js +2 -1
  112. package/build/src/contract-guidance.d.ts +5 -0
  113. package/build/src/contract-guidance.js +121 -0
  114. package/build/src/contract-worktree.d.ts +0 -1
  115. package/build/src/contract-worktree.js +32 -83
  116. package/build/src/coordination/sqlite-transaction-lock.d.ts +1 -0
  117. package/build/src/coordination/sqlite-transaction-lock.js +5 -0
  118. package/build/src/core/facts/codec.d.ts +2 -0
  119. package/build/src/core/facts/codec.js +4 -0
  120. package/build/src/core/verbs/placement.d.ts +6 -1
  121. package/build/src/core/verbs/placement.js +5 -3
  122. package/build/src/dispatch/index.d.ts +0 -2
  123. package/build/src/dispatch/index.js +54 -55
  124. package/build/src/git/admission.js +2 -2
  125. package/build/src/git/hooks.d.ts +9 -15
  126. package/build/src/git/hooks.js +23 -317
  127. package/build/src/git/nuke.d.ts +2 -1
  128. package/build/src/git/nuke.js +36 -18
  129. package/build/src/git/private-state-seat.d.ts +12 -0
  130. package/build/src/git/private-state-seat.js +39 -0
  131. package/build/src/git/process.d.ts +6 -0
  132. package/build/src/git/process.js +19 -7
  133. package/build/src/git/read-observation.js +29 -20
  134. package/build/src/git/reconcile.d.ts +11 -0
  135. package/build/src/git/reconcile.js +64 -11
  136. package/build/src/git/repository.d.ts +6 -2
  137. package/build/src/git/repository.js +8 -0
  138. package/build/src/git/target-placement.d.ts +1 -0
  139. package/build/src/git/target-placement.js +5 -0
  140. package/build/src/git/tender.d.ts +1 -0
  141. package/build/src/git/terminal-reconcile.d.ts +1 -1
  142. package/build/src/git/terminal-reconcile.js +22 -20
  143. package/build/src/git/workspace.d.ts +14 -0
  144. package/build/src/git/workspace.js +44 -0
  145. package/build/src/kanshi/index.d.ts +1 -1
  146. package/build/src/kanshi/read.js +16 -16
  147. package/build/src/kanshi/report.d.ts +2 -2
  148. package/build/src/library/address.d.ts +9 -3
  149. package/build/src/library/address.js +3 -2
  150. package/build/src/library/akuma-creation.d.ts +4 -3
  151. package/build/src/library/akuma-creation.js +19 -13
  152. package/build/src/library/audit.d.ts +1 -1
  153. package/build/src/library/audit.js +5 -3
  154. package/build/src/library/catalog.d.ts +3 -1
  155. package/build/src/library/catalog.js +20 -6
  156. package/build/src/library/composition.d.ts +371 -0
  157. package/build/src/library/composition.js +28 -0
  158. package/build/src/library/configuration.js +8 -2
  159. package/build/src/library/continuation.js +66 -30
  160. package/build/src/library/contract-bind.js +10 -2
  161. package/build/src/library/contract-forwarding-reconciliation-result.d.ts +96 -0
  162. package/build/src/library/contract-forwarding-reconciliation-result.js +83 -0
  163. package/build/src/library/contract-forwarding-result.d.ts +6723 -0
  164. package/build/src/library/contract-forwarding-result.js +521 -0
  165. package/build/src/library/contract-forwarding.d.ts +72 -0
  166. package/build/src/library/contract-forwarding.js +125 -0
  167. package/build/src/library/contract-handle.d.ts +36 -0
  168. package/build/src/library/contract-handle.js +313 -0
  169. package/build/src/library/contract-operations.d.ts +1044 -67
  170. package/build/src/library/contract-operations.js +190 -118
  171. package/build/src/library/contract.d.ts +11 -32
  172. package/build/src/library/contract.js +16 -289
  173. package/build/src/library/fleet-result.d.ts +1454 -0
  174. package/build/src/library/fleet-result.js +96 -0
  175. package/build/src/library/fleet.d.ts +112 -54
  176. package/build/src/library/fleet.js +211 -71
  177. package/build/src/library/input.d.ts +2 -0
  178. package/build/src/library/input.js +4 -0
  179. package/build/src/library/keiyaku.d.ts +371 -20
  180. package/build/src/library/keiyaku.js +8 -21
  181. package/build/src/library/mutation.d.ts +3 -3
  182. package/build/src/library/mutation.js +5 -9
  183. package/build/src/library/nuke.d.ts +1 -1
  184. package/build/src/library/nuke.js +4 -3
  185. package/build/src/library/reconcile.d.ts +4 -0
  186. package/build/src/library/reconcile.js +2 -0
  187. package/build/src/library/refusal.d.ts +0 -9
  188. package/build/src/library/refusal.js +0 -7
  189. package/build/src/protocol/amend.js +56 -50
  190. package/build/src/protocol/attempt.d.ts +2 -0
  191. package/build/src/protocol/attempt.js +8 -1
  192. package/build/src/protocol/completion.js +7 -0
  193. package/build/src/protocol/deliver.js +6 -2
  194. package/build/src/protocol/operations.d.ts +4 -1
  195. package/build/src/protocol/operations.js +5 -2
  196. package/build/src/protocol/placement.d.ts +1 -0
  197. package/build/src/protocol/placement.js +52 -38
  198. package/build/src/protocol/read/observation.d.ts +1 -4
  199. package/build/src/protocol/read/observation.js +2 -15
  200. package/build/src/protocol/reintegrate.js +62 -58
  201. package/build/src/protocol/review.js +5 -0
  202. package/build/src/protocol/run.js +17 -13
  203. package/build/src/runtime/proc/run.d.ts +8 -0
  204. package/build/src/runtime/proc/run.js +27 -0
  205. package/build/src/runtime/proc/termination.js +26 -22
  206. package/build/src/runtime/proc/windows-launch.exe +0 -0
  207. package/build/src/settlement/holder.d.ts +2 -1
  208. package/build/src/settlement/holder.js +15 -4
  209. package/build/src/settlement/settle.js +99 -33
  210. package/build/src/task/board.d.ts +68 -27
  211. package/build/src/task/board.js +31 -1
  212. package/build/src/task/catalog.d.ts +4 -0
  213. package/build/src/task/catalog.js +23 -0
  214. package/build/src/task/compose-language.js +33 -7
  215. package/build/src/task/compose-parser.d.ts +1 -1
  216. package/build/src/task/compose-parser.js +1 -1
  217. package/build/src/task/compose.js +2 -0
  218. package/build/src/task/index.d.ts +12 -4
  219. package/build/src/task/index.js +97 -16
  220. package/build/src/task/mutation-result.d.ts +923 -0
  221. package/build/src/task/mutation-result.js +140 -0
  222. package/build/src/task/mutation.d.ts +552 -28
  223. package/build/src/task/mutation.js +304 -97
  224. package/build/src/task/operations.d.ts +1 -4
  225. package/build/src/task/operations.js +0 -6
  226. package/build/src/task/public.d.ts +2 -0
  227. package/build/src/task/public.js +1 -0
  228. package/build/src/workspace-place.d.ts +7 -2
  229. package/build/src/workspace-place.js +34 -24
  230. package/build/src/world.d.ts +1 -0
  231. package/build/src/world.js +11 -0
  232. package/package.json +8 -6
  233. package/build/src/akuma/request-execution.d.ts +0 -8
  234. package/build/src/akuma/request-execution.js +0 -98
@@ -3,94 +3,18 @@ import { join } from "node:path";
3
3
  import { acquireSqliteTransactionLock } from "./coordination/sqlite-transaction-lock.js";
4
4
  import { repairDerivedFile } from "./coordination/durable-file.js";
5
5
  import { contractId } from "./core/facts/types.js";
6
+ import { CONTRACT_DELIVERER_SKILL, CONTRACT_REVIEWER_SKILL, renderContractAppointment, renderContractGuidance, } from "./contract-guidance.js";
6
7
  import { worktreeGitDirectory, worktreeRoot } from "./git/repository.js";
7
8
  import { GitPlumbingError, runGit } from "./git/process.js";
8
9
  import { worktreePath } from "./git/workspace.js";
9
10
  import { appointmentFor, placeRegisterPath } from "./workspace-place.js";
10
11
  const IGNORE_BYTES = ".gitignore\nKEIYAKU.md\n";
11
12
  const PRIMARY_IGNORE_BYTES = "*\n!settings.json\n!tasks/\n!tasks/**\n";
12
- const APPOINTMENT_DESCRIPTION = "This is a read-only projection. Do not edit manually.";
13
- function appointmentBytes(contract) {
14
- return `---\ncontract: ${contract}\ndescription: ${APPOINTMENT_DESCRIPTION}\n---\n`;
15
- }
16
- function renderArc(arc) {
17
- return [
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(),
35
- ].join("\n");
36
- }
37
- const FULFILLMENT = [
38
- "## Fulfillment",
39
- "",
40
- "### Appointment",
41
- "",
42
- "Each commission names exactly one seat: Deliverer or Reviewer.",
43
- "If no seat was named, stop and ask the caller. Never infer it.",
44
- "",
45
- "### Worktree",
46
- "",
47
- "This file is a derived view of the journal-authoritative Contract. Never edit it to change the Contract.",
48
- "Treat the directory containing `.keiyaku/KEIYAKU.md` as the Contract worktree root.",
49
- "Read the complete Contract before acting and keep work inside that worktree.",
50
- "",
51
- "### Deliverer",
52
- "",
53
- "Re-read this file on each wake-up; it may have been updated between sessions.",
54
- "Implement and verify the Objective under the Design, Region, and Criteria in this Contract.",
55
- "Region is a rough intent, not a hard boundary — natural ripple outside it is fine and not a blocker.",
56
- "When an Arc is active, stay within that current chapter.",
57
- "If you discover a contradiction with this Contract or an unclosed point in its terms,",
58
- "stop and report it before continuing.",
59
- "",
60
- "For work requiring three or more steps, prefer `keiyaku task -C <worktree>` to organize and manage Tasks.",
61
- "Promptly update progress for Tasks already present in the current worktree.",
62
- "",
63
- "Deliver from this worktree. A clean worktree delivers HEAD; uncommitted work",
64
- "needs `deliver --include-dirty`, which captures the final non-ignored tree and",
65
- "stages or commits nothing. If deliver reports a conflict, run",
66
- "`deliver --materialize-conflict`, resolve the conflicted files, stage them,",
67
- "and deliver again; while the merge stays uncommitted that continuation uses",
68
- "`--include-dirty`. Unresolved paths always refuse.",
69
- "",
70
- "### Reviewer",
71
- "",
72
- "Re-read this file on each wake-up; it may have been updated between sessions.",
73
- "Review the complete current worktree snapshot against this Contract. Do not modify it.",
74
- "Understand the original intent behind the Objective, not just its literal text.",
75
- "Region is a rough intent, not a hard boundary — natural ripple outside it is not a blocker.",
76
- "Compare each Criterion individually against the delivered work.",
77
- "Consolidate findings by root cause — group symptoms that share an underlying issue",
78
- "rather than listing each occurrence separately.",
79
- "Dig to the architectural or structural root; flag it directly rather than routing around it.",
80
- "If you discover a contradiction within the Contract or an unclosed point in its terms,",
81
- "report it as a finding.",
82
- "Report covered Criteria, findings, and missing evidence.",
83
- ].join("\n");
84
- export function renderContractGuidance(state) {
85
- return [
86
- appointmentBytes(state.id).trimEnd(),
87
- state.terms.document.bytes.trimEnd(),
88
- ...(state.currentArc === undefined ? [] : [renderArc(state.currentArc.data)]),
89
- FULFILLMENT,
90
- ]
91
- .join("\n\n")
92
- .concat("\n");
93
- }
13
+ const SEAT_IGNORE_BYTES = ".gitignore\nSKILL.md\n";
14
+ const SEAT_SKILLS = [
15
+ ["keiyaku-deliver", CONTRACT_DELIVERER_SKILL],
16
+ ["keiyaku-review", CONTRACT_REVIEWER_SKILL],
17
+ ];
94
18
  async function isTracked(repository, relativePath) {
95
19
  try {
96
20
  await runGit(repository, ["ls-files", "--error-unmatch", "--", relativePath]);
@@ -145,7 +69,7 @@ export async function releaseContractWorktree(repository, contract) {
145
69
  const appointment = await readContractAppointment(repository);
146
70
  if (appointment.kind !== "appointed" || appointment.contract !== contract)
147
71
  return;
148
- if ((await readFile(appointment.path, "utf8")) !== appointmentBytes(contract))
72
+ if ((await readFile(appointment.path, "utf8")) !== renderContractAppointment(contract))
149
73
  return;
150
74
  try {
151
75
  await unlink(appointment.path);
@@ -169,6 +93,23 @@ async function repair(repository, worktree, relativePath, bytes) {
169
93
  }
170
94
  return { kind: "contract-file", path, action };
171
95
  }
96
+ async function isKeiyakuSeatLeaf(worktree, seat) {
97
+ const sentinel = join(worktree, ".agents", "skills", seat, ".gitignore");
98
+ const stat = await lstat(sentinel).catch((error) => error.code === "ENOENT" ? undefined : Promise.reject(error));
99
+ return stat?.isFile() === true && !stat.isSymbolicLink() && (await readFile(sentinel, "utf8")) === SEAT_IGNORE_BYTES;
100
+ }
101
+ async function materializeSeatSkill(repository, worktree, seat, bytes) {
102
+ const leaf = `.agents/skills/${seat}`;
103
+ const skill = `${leaf}/SKILL.md`;
104
+ const skillPath = join(worktree, skill);
105
+ const skillExists = await lstat(skillPath).catch((error) => error.code === "ENOENT" ? false : Promise.reject(error));
106
+ if (!(await isKeiyakuSeatLeaf(worktree, seat)) && skillExists)
107
+ return [];
108
+ return [
109
+ await repair(repository, worktree, `${leaf}/.gitignore`, SEAT_IGNORE_BYTES),
110
+ await repair(repository, worktree, skill, bytes),
111
+ ];
112
+ }
172
113
  async function materialize(repository, worktree, guidance) {
173
114
  const effects = [];
174
115
  const failed = (target, path, error) => ({
@@ -218,6 +159,14 @@ async function materialize(repository, worktree, guidance) {
218
159
  return failed(worktree, join(worktree, relativePath), error);
219
160
  }
220
161
  }
162
+ for (const [seat, bytes] of SEAT_SKILLS) {
163
+ try {
164
+ effects.push(...(await materializeSeatSkill(scoped, worktree, seat, bytes)));
165
+ }
166
+ catch (error) {
167
+ return failed(worktree, join(worktree, ".agents", "skills", seat), error);
168
+ }
169
+ }
221
170
  return { effects, lag: [] };
222
171
  }
223
172
  export async function projectContractWorktree(repository, state, register, _onConflict) {
@@ -11,6 +11,7 @@ export declare function acquireSqliteTransactionLock(input: {
11
11
  mode: SqliteTransactionLockMode;
12
12
  timeoutMs?: number;
13
13
  signal?: AbortSignal;
14
+ onContended?: () => void;
14
15
  }): Promise<HeldSqliteTransactionLock>;
15
16
  export declare function tryAcquireSqliteTransactionLock(input: {
16
17
  path: string;
@@ -119,6 +119,7 @@ export async function acquireSqliteTransactionLock(input) {
119
119
  }
120
120
  const started = performance.now();
121
121
  let cap = INITIAL_RETRY_MS;
122
+ let contended = false;
122
123
  for (;;) {
123
124
  input.signal?.throwIfAborted();
124
125
  try {
@@ -127,6 +128,10 @@ export async function acquireSqliteTransactionLock(input) {
127
128
  catch (error) {
128
129
  if (!isBusy(error))
129
130
  throw error;
131
+ if (!contended) {
132
+ contended = true;
133
+ input.onContended?.();
134
+ }
130
135
  const elapsed = performance.now() - started;
131
136
  if (input.timeoutMs !== undefined && elapsed >= input.timeoutMs) {
132
137
  throw new SqliteTransactionLockError(`SQLite lock timed out after ${input.timeoutMs}ms: ${input.path}`, "timeout", { cause: error });
@@ -1,3 +1,5 @@
1
1
  import type { JournalEntry } from "./types.js";
2
+ /** Validate one in-memory journal value with the persisted-fact authority. */
3
+ export declare function decodeJournalEntry(value: unknown): JournalEntry;
2
4
  export declare function encodeEntry(entry: JournalEntry): string;
3
5
  export declare function decodeJournal(journal: string): JournalEntry[];
@@ -235,6 +235,10 @@ function validateEntry(value) {
235
235
  }
236
236
  return entry;
237
237
  }
238
+ /** Validate one in-memory journal value with the persisted-fact authority. */
239
+ export function decodeJournalEntry(value) {
240
+ return validateEntry(value);
241
+ }
238
242
  function canonicalValue(value) {
239
243
  if (value === null)
240
244
  return "null";
@@ -1,3 +1,4 @@
1
+ import { type GateReport } from "../facts/gate.js";
1
2
  import type { ActorId, ContractId } from "../facts/types.js";
2
3
  import type { DecideInput, OfferDecision } from "../decide.js";
3
4
  type PlacementInput = Readonly<{
@@ -10,8 +11,12 @@ export type UnmetPrerequisite = Readonly<{
10
11
  state: "missing" | "active" | "abandoned";
11
12
  }>;
12
13
  export type PlacementRefusal = Readonly<{
13
- kind: "contract-missing" | "delivery-missing" | "terminal" | "gates-unsatisfied";
14
+ kind: "contract-missing" | "delivery-missing" | "terminal";
14
15
  contractId: ContractId;
16
+ }> | Readonly<{
17
+ kind: "gates-unsatisfied";
18
+ contractId: ContractId;
19
+ unmet: readonly GateReport[];
15
20
  }> | Readonly<{
16
21
  kind: "prerequisites-unsatisfied";
17
22
  contractId: ContractId;
@@ -1,4 +1,4 @@
1
- import { gatesSatisfied } from "../facts/gate.js";
1
+ import { gateReports } from "../facts/gate.js";
2
2
  import { activeContract, contractState } from "../facts/observation.js";
3
3
  function unmetPrerequisites(prerequisites, observation) {
4
4
  const unmet = [];
@@ -30,8 +30,10 @@ export function decidePlacement({ input, attempt, observation, }) {
30
30
  if (unmet.length > 0) {
31
31
  return { kind: "refused", refusal: { kind: "prerequisites-unsatisfied", contractId: id, unmet } };
32
32
  }
33
- if (!gatesSatisfied(current)) {
34
- return { kind: "refused", refusal: { kind: "gates-unsatisfied", contractId: id } };
33
+ const gates = gateReports(current);
34
+ if (!gates.satisfied) {
35
+ const unmetGates = gates.reports.filter((report) => report.current.kind !== "attested" || report.current.verdict !== "satisfied");
36
+ return { kind: "refused", refusal: { kind: "gates-unsatisfied", contractId: id, unmet: unmetGates } };
35
37
  }
36
38
  const claimed = {
37
39
  v: 1,
@@ -10,8 +10,6 @@ export type Dispatch = Readonly<{
10
10
  export type DispatchFailure = Readonly<{
11
11
  kind: "conflict";
12
12
  current: Dispatch;
13
- }> | Readonly<{
14
- kind: "contention";
15
13
  }> | Readonly<{
16
14
  kind: "publication-failed";
17
15
  diagnostic: string;
@@ -1,15 +1,13 @@
1
1
  import { createHash } from "node:crypto";
2
- import { join } from "node:path";
3
2
  import { AuthorityCorruptionError } from "../core/facts/errors.js";
4
3
  import { contractId } from "../core/facts/types.js";
5
4
  import { parseAkuId } from "../akuma/identity.js";
6
- import { acquireSqliteTransactionLock } from "../coordination/sqlite-transaction-lock.js";
7
- import { GIT_FORMAT_BYTES, GIT_FORMAT_PATH, GIT_REF, readBlob, readGitPaths, updateGitTree, updateRefsAtomically, writeBlob, writeCommit, } from "../git/repository.js";
5
+ import { confirmPrivateStatePublication, withPrivateStatePublicationSeat } from "../git/private-state-seat.js";
6
+ import { GIT_FORMAT_BYTES, GIT_FORMAT_PATH, GIT_REF, readBlob, readGitPaths, updateGitTree, updateRefsAtomically, writeBlob, writeStateCommit, } from "../git/repository.js";
8
7
  import { withGitDecodeChannel, withGitReadObservation } from "../git/read-observation.js";
9
8
  const DISPATCH_ROOT = "dispatch";
10
9
  const DISPATCH_PREFIX = `${DISPATCH_ROOT}/`;
11
10
  const DISPATCH_SUFFIX = ".json";
12
- const MAX_ATTEMPTS = 3;
13
11
  function pathFor(akuId) {
14
12
  return `${DISPATCH_PREFIX}${createHash("sha256").update(akuId).digest("hex")}${DISPATCH_SUFFIX}`;
15
13
  }
@@ -125,57 +123,58 @@ export async function publishDispatch(input) {
125
123
  const owner = contractId(input.contractId);
126
124
  const intended = { akuId, contractId: owner, dispatchedAt: new Date().toISOString() };
127
125
  const path = pathFor(akuId);
128
- const held = await acquireSqliteTransactionLock({
129
- path: join(input.repository.commonDirectory, "keiyaku", "locks", "dispatch.sqlite"),
130
- mode: "immediate",
131
- });
132
- try {
133
- for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt += 1) {
134
- const snapshot = await readGitPaths(input.repository, [path]);
135
- const current = await dispatchFromSnapshot(input.repository, snapshot, akuId);
136
- if (current !== null) {
137
- return current.contractId === owner
138
- ? { kind: "dispatched", dispatch: current }
139
- : { kind: "failed", failure: { kind: "conflict", current } };
140
- }
141
- const changes = new Map();
142
- if (snapshot.commit === null) {
143
- changes.set(GIT_FORMAT_PATH, { oid: await writeBlob(input.repository, GIT_FORMAT_BYTES) });
144
- }
145
- changes.set(path, { oid: await writeBlob(input.repository, bytesFor(intended)) });
146
- const tree = await updateGitTree(input.repository, snapshot.tree, changes);
147
- const commit = await writeCommit({
148
- repository: input.repository,
149
- tree,
150
- parent: snapshot.commit,
151
- message: `dispatch ${akuId}`,
152
- at: intended.dispatchedAt,
153
- });
154
- const publication = await updateRefsAtomically(input.repository, [
155
- {
156
- ref: GIT_REF,
157
- newOid: commit,
158
- expectedOid: snapshot.commit,
159
- },
160
- ]);
161
- if (publication.kind === "published")
162
- return { kind: "dispatched", dispatch: intended };
163
- const observed = await observedPublication(input.repository, akuId, owner);
164
- if (observed !== null)
165
- return observed;
166
- if (publication.kind === "non-published") {
167
- const fresh = await readGitPaths(input.repository, [path]);
168
- if (fresh.commit === snapshot.commit) {
169
- return {
170
- kind: "failed",
171
- failure: { kind: "publication-failed", diagnostic: diagnostic(publication.error) },
172
- };
173
- }
126
+ return await withPrivateStatePublicationSeat(input.repository, async (seat) => {
127
+ const snapshot = await readGitPaths(input.repository, [path]);
128
+ const current = await dispatchFromSnapshot(input.repository, snapshot, akuId);
129
+ if (current !== null) {
130
+ if (current.contractId === owner)
131
+ confirmPrivateStatePublication(seat);
132
+ return current.contractId === owner
133
+ ? { kind: "dispatched", dispatch: current }
134
+ : { kind: "failed", failure: { kind: "conflict", current } };
135
+ }
136
+ const changes = new Map();
137
+ if (snapshot.commit === null) {
138
+ changes.set(GIT_FORMAT_PATH, { oid: await writeBlob(input.repository, GIT_FORMAT_BYTES) });
139
+ }
140
+ changes.set(path, { oid: await writeBlob(input.repository, bytesFor(intended)) });
141
+ const tree = await updateGitTree(input.repository, snapshot.tree, changes);
142
+ const commit = await writeStateCommit({
143
+ repository: input.repository,
144
+ tree,
145
+ parent: snapshot.commit,
146
+ message: `dispatch ${akuId}`,
147
+ at: intended.dispatchedAt,
148
+ });
149
+ const publication = await updateRefsAtomically(input.repository, [
150
+ {
151
+ ref: GIT_REF,
152
+ newOid: commit,
153
+ expectedOid: snapshot.commit,
154
+ },
155
+ ]);
156
+ if (publication.kind === "published") {
157
+ confirmPrivateStatePublication(seat);
158
+ return { kind: "dispatched", dispatch: intended };
159
+ }
160
+ const observed = await observedPublication(input.repository, akuId, owner);
161
+ if (observed !== null) {
162
+ if (observed.kind === "dispatched")
163
+ confirmPrivateStatePublication(seat);
164
+ return observed;
165
+ }
166
+ if (publication.kind === "non-published") {
167
+ const fresh = await readGitPaths(input.repository, [path]);
168
+ if (fresh.commit === snapshot.commit) {
169
+ return {
170
+ kind: "failed",
171
+ failure: { kind: "publication-failed", diagnostic: diagnostic(publication.error) },
172
+ };
174
173
  }
175
174
  }
176
- return { kind: "failed", failure: { kind: "contention" } };
177
- }
178
- finally {
179
- held.close();
180
- }
175
+ return {
176
+ kind: "failed",
177
+ failure: { kind: "publication-failed", diagnostic: "Dispatch publication outcome is unknown" },
178
+ };
179
+ });
181
180
  }
@@ -1,5 +1,5 @@
1
1
  import { decodeJournal, encodeEntry } from "../core/facts/codec.js";
2
- import { GIT_FORMAT_BYTES, GIT_FORMAT_PATH, GIT_REF, updateGitTreeFromFrozenDirectories, updateRefsAtomically, writeBlob, writeCommit, } from "./repository.js";
2
+ import { GIT_FORMAT_BYTES, GIT_FORMAT_PATH, GIT_REF, updateGitTreeFromFrozenDirectories, updateRefsAtomically, writeBlob, writeStateCommit, } from "./repository.js";
3
3
  import { contractJournalPath, contractJournalPaths, gitObjectIdForSnapshot, mintContractHead, mintSnapshotId, } from "./identity.js";
4
4
  import { validPath } from "./tree.js";
5
5
  const CONTRACT_JOURNAL_PATH = /^contracts\/(?:active|terminal)\/[0-9a-f]{2}\/[0-9a-f]{2}\/[0-9a-f]{60}\.jsonl$/u;
@@ -86,7 +86,7 @@ async function buildOffer(repository, admission, appends, companions) {
86
86
  async function publishOffer(repository, admission, target, changes, assertions) {
87
87
  const { snapshot } = admission;
88
88
  const gitTree = await updateGitTreeFromFrozenDirectories(repository, snapshot.tree, admission.treeDirectories, changes);
89
- const gitCommit = mintSnapshotId(await writeCommit({
89
+ const gitCommit = mintSnapshotId(await writeStateCommit({
90
90
  repository,
91
91
  tree: gitTree,
92
92
  parent: snapshot.commit,
@@ -1,4 +1,5 @@
1
1
  export type HookCommand = Readonly<{
2
+ name: string;
2
3
  argv: readonly string[];
3
4
  timeoutMs: number;
4
5
  }>;
@@ -25,18 +26,10 @@ export type WorktreeHookLag = Readonly<{
25
26
  phase: HookPhase;
26
27
  path: string;
27
28
  command: number;
29
+ name: string;
28
30
  failure: HookFailure;
29
31
  }>;
30
- type HookPhase = "create" | "destroy";
31
- export declare function hookMarkerPath(administrationDirectory: string): string;
32
- export type WorktreeHookMarkerObservation = Readonly<{
33
- kind: "absent" | "pending" | "ok";
34
- }> | Readonly<{
35
- kind: "failed";
36
- diagnostic: string;
37
- }>;
38
- /** Read the durable hook marker without executing commands or taking locks. */
39
- export declare function observeWorktreeHookMarker(administrationDirectory: string): Promise<WorktreeHookMarkerObservation>;
32
+ export type HookPhase = "create" | "destroy";
40
33
  export type HookCommandRun = Readonly<{
41
34
  kind: "ok";
42
35
  }> | Readonly<{
@@ -48,8 +41,9 @@ export type HookCommandRun = Readonly<{
48
41
  }>;
49
42
  /** Execute an ordered command list without lifecycle state. Scratch owns this policy. */
50
43
  export declare function runHookCommands(worktree: string, commands: readonly HookCommand[], signal?: AbortSignal): Promise<HookCommandRun>;
51
- /** Remove validated Keiyaku hook state after its worktree has been reset. */
52
- export declare function nukeWorktreeHookResidue(administrationDirectory: string): Promise<void>;
53
- export declare function runCreateHooks(worktree: string, administrationDirectory: string, hooks: WorktreeHooks, retryHooks: boolean): Promise<WorktreeHookLag | null>;
54
- export declare function runDestroyHooks(worktree: string, administrationDirectory: string, hooks: WorktreeHooks, retryHooks: boolean): Promise<WorktreeHookLag | null>;
55
- export {};
44
+ export type HookPhaseRun = Readonly<{
45
+ lag: WorktreeHookLag | null;
46
+ runs: readonly string[];
47
+ }>;
48
+ export declare function runCreateHooks(worktree: string, hooks: WorktreeHooks): Promise<HookPhaseRun>;
49
+ export declare function runDestroyHooks(worktree: string, hooks: WorktreeHooks): Promise<HookPhaseRun>;