@astrosheep/keiyaku 2.9.11 → 2.9.13

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 (218) hide show
  1. package/README.md +79 -85
  2. package/build/.tsbuildinfo +1 -1
  3. package/build/agents/call-terms.js +1 -0
  4. package/build/agents/providers/claude-agent-sdk/adapter.js +17 -4
  5. package/build/cli/commands/akuma/akuma/handler.js +2 -1
  6. package/build/cli/commands/akuma/akuma/meta.js +4 -3
  7. package/build/cli/commands/akuma/catalog.js +2 -0
  8. package/build/cli/commands/akuma/list/handler.js +1 -1
  9. package/build/cli/commands/akuma/list/meta-list.js +12 -0
  10. package/build/cli/commands/akuma/list/meta-ls.js +2 -2
  11. package/build/cli/commands/akuma.js +5 -0
  12. package/build/cli/commands/contract/amend/meta.js +4 -3
  13. package/build/cli/commands/contract/arc/meta.js +2 -0
  14. package/build/cli/commands/contract/audit/handler.js +28 -2
  15. package/build/cli/commands/contract/audit/meta.js +4 -3
  16. package/build/cli/commands/contract/bind/meta.js +4 -4
  17. package/build/cli/commands/contract/petition/handler.js +2 -1
  18. package/build/cli/commands/contract/petition/meta.js +2 -2
  19. package/build/cli/commands/contract/renew/handler.js +44 -3
  20. package/build/cli/commands/contract/renew/meta.js +5 -4
  21. package/build/cli/commands/metadata.js +14 -10
  22. package/build/cli/commands/projection/call/handler.js +1 -1
  23. package/build/cli/commands/projection/call/meta.js +1 -1
  24. package/build/cli/commands/projection/catalog.js +2 -2
  25. package/build/cli/commands/projection/{revive → fork}/handler.js +10 -5
  26. package/build/cli/commands/projection/fork/meta.js +12 -0
  27. package/build/cli/commands/projection/history/handler.js +46 -2
  28. package/build/cli/commands/projection/history/meta.js +3 -3
  29. package/build/cli/commands/projection/kill/handler.js +4 -5
  30. package/build/cli/commands/projection/kill/meta.js +2 -2
  31. package/build/cli/commands/projection/status/handler.js +29 -13
  32. package/build/cli/commands/projection/status/meta.js +14 -3
  33. package/build/cli/commands/projection/tell/handler.js +8 -4
  34. package/build/cli/commands/projection/tell/meta.js +1 -1
  35. package/build/cli/commands/projection/wait/handler.js +45 -20
  36. package/build/cli/commands/projection/wait/meta.js +1 -1
  37. package/build/cli/commands/shared.js +2 -2
  38. package/build/cli/commands/system/catalog.js +2 -0
  39. package/build/cli/commands/task/add/handler.js +28 -12
  40. package/build/cli/commands/task/add/meta.js +18 -6
  41. package/build/cli/commands/task/catalog.js +4 -0
  42. package/build/cli/commands/task/compose/handler.js +18 -0
  43. package/build/cli/commands/task/compose/meta.js +10 -0
  44. package/build/cli/commands/task/doctor/handler.js +2 -3
  45. package/build/cli/commands/task/doctor/meta.js +2 -2
  46. package/build/cli/commands/task/done/meta.js +2 -2
  47. package/build/cli/commands/task/drop/handler.js +7 -2
  48. package/build/cli/commands/task/drop/meta.js +6 -3
  49. package/build/cli/commands/task/hold/meta.js +2 -2
  50. package/build/cli/commands/task/log/meta.js +2 -2
  51. package/build/cli/commands/task/ls/meta.js +2 -2
  52. package/build/cli/commands/task/note/handler.js +6 -0
  53. package/build/cli/commands/task/note/meta.js +11 -0
  54. package/build/cli/commands/task/resume/meta.js +2 -2
  55. package/build/cli/commands/task/shared.js +29 -15
  56. package/build/cli/commands/task/show/meta.js +2 -2
  57. package/build/cli/commands/task/start/meta.js +2 -2
  58. package/build/cli/commands/task/stop/meta.js +2 -2
  59. package/build/cli/commands/task/update/meta.js +3 -2
  60. package/build/cli/commands/verification/handler.js +43 -0
  61. package/build/cli/commands/verification/meta.js +13 -0
  62. package/build/cli/completion.js +16 -13
  63. package/build/cli/flags.js +48 -12
  64. package/build/cli/help.js +29 -30
  65. package/build/cli/index.js +14 -8
  66. package/build/cli/parse-flags.js +60 -21
  67. package/build/cli/parse-metadata.js +10 -4
  68. package/build/cli/parse-selectors.js +22 -4
  69. package/build/cli/parse.js +43 -30
  70. package/build/cli/projection-address.js +7 -7
  71. package/build/cli/render/arc.js +12 -3
  72. package/build/cli/render/audit.js +2 -2
  73. package/build/cli/render/call.js +4 -4
  74. package/build/cli/render/kanshi.js +122 -85
  75. package/build/cli/render/misc.js +2 -2
  76. package/build/cli/render/petition.js +16 -1
  77. package/build/cli/render/projection-history.js +17 -4
  78. package/build/cli/render/shared.js +5 -5
  79. package/build/cli/render/status-indicator.js +47 -0
  80. package/build/cli/render/status.js +106 -76
  81. package/build/cli/render/success-response.js +20 -5
  82. package/build/cli/render/task.js +33 -5
  83. package/build/cli/render/tell.js +10 -4
  84. package/build/cli/render/tool-command-normalization.js +137 -0
  85. package/build/cli/render/tool-presentation.js +2 -1
  86. package/build/cli/render/verification.js +23 -0
  87. package/build/cli/render/wait.js +101 -33
  88. package/build/cli/types.js +2 -1
  89. package/build/config/akuma-loader.js +21 -1
  90. package/build/config/settings/knobs.js +7 -1
  91. package/build/config/settings/schema.js +10 -5
  92. package/build/core/addressing.js +3 -3
  93. package/build/core/amend.js +102 -17
  94. package/build/core/arc.js +4 -4
  95. package/build/core/audit/candidate.js +10 -18
  96. package/build/core/audit/coordinates.js +12 -17
  97. package/build/core/audit/evidence.js +6 -4
  98. package/build/core/audit/facade.js +3 -3
  99. package/build/core/audit/report.js +1 -1
  100. package/build/core/bind-reconciliation.js +42 -23
  101. package/build/core/bind.js +182 -81
  102. package/build/core/call/call.js +6 -7
  103. package/build/core/call/context.js +46 -33
  104. package/build/core/call/execution.js +21 -6
  105. package/build/core/call/prompt.js +11 -42
  106. package/build/core/contract-carrier-runtime.js +209 -105
  107. package/build/core/contract-carrier.js +156 -73
  108. package/build/core/contract-view.js +43 -6
  109. package/build/core/contract.js +2 -1
  110. package/build/core/derived-replay.js +168 -35
  111. package/build/core/draft.js +60 -27
  112. package/build/core/forfeit.js +1 -1
  113. package/build/core/lifecycle-history.js +13 -0
  114. package/build/core/lifecycle-recovery.js +32 -27
  115. package/build/core/lifecycle-runner.js +14 -14
  116. package/build/core/log.js +1 -1
  117. package/build/core/outcome-base.js +2 -2
  118. package/build/core/projection/akuma-name.js +43 -0
  119. package/build/core/projection/generation/database.js +141 -79
  120. package/build/core/projection/generation/ledger.js +91 -0
  121. package/build/core/projection/generation/model.js +19 -14
  122. package/build/core/projection/generation/projection-generation-continuation.js +1 -1
  123. package/build/core/projection/generation/projection-generation-doorbell.js +88 -0
  124. package/build/core/projection/generation/projection-generation-execution.js +24 -0
  125. package/build/core/projection/generation/projection-generation-identity.js +4 -0
  126. package/build/core/projection/generation/projection-generation-launcher.js +72 -138
  127. package/build/core/projection/generation/projection-generation-process.js +16 -15
  128. package/build/core/projection/generation/projection-generation-runner.js +132 -76
  129. package/build/core/projection/generation/projection-generation-runtime.js +10 -61
  130. package/build/core/projection/generation/protocol.js +68 -0
  131. package/build/core/projection/generation/store.js +17 -7
  132. package/build/core/projection/generation/transitions.js +24 -14
  133. package/build/core/projection/index.js +5 -5
  134. package/build/core/projection/projection-core.js +6 -2
  135. package/build/core/projection/projection-execution-observer.js +1 -1
  136. package/build/core/projection/projection-history.js +8 -8
  137. package/build/core/projection/projection-kill.js +20 -10
  138. package/build/core/projection/projection-life-observer.js +8 -8
  139. package/build/core/projection/projection-mint.js +6 -8
  140. package/build/core/projection/projection-status-observation.js +77 -8
  141. package/build/core/projection/projection-status.js +151 -4
  142. package/build/core/projection/projection-wait.js +48 -29
  143. package/build/core/projection/projection-wake.js +28 -88
  144. package/build/core/projection/tell/launch-store.js +1 -1
  145. package/build/core/projection/tell/store.js +1 -1
  146. package/build/core/renew-build.js +204 -74
  147. package/build/core/renew-plan.js +34 -9
  148. package/build/core/renew-rewrite.js +56 -15
  149. package/build/core/renew.js +39 -35
  150. package/build/core/repository-ledger/accepted-fold-read.js +6 -3
  151. package/build/core/repository-ledger/claim-evidence.js +1 -1
  152. package/build/core/repository-ledger/codec.js +11 -5
  153. package/build/core/repository-ledger/current-state-store.js +464 -349
  154. package/build/core/repository-ledger/fold-repository.js +5 -6
  155. package/build/core/repository-ledger/identity.js +60 -0
  156. package/build/core/repository-ledger/inventory.js +3 -3
  157. package/build/core/repository-ledger/read-model.js +57 -9
  158. package/build/core/repository-ledger/write-transaction.js +24 -17
  159. package/build/core/run-control/connection-bound-close.js +41 -0
  160. package/build/core/run-control/detached-lease-bootstrap.js +208 -0
  161. package/build/core/run-control/detached-process.js +14 -0
  162. package/build/core/run-control/index.js +6 -0
  163. package/build/core/{process-group.js → run-control/process-tree.js} +1 -1
  164. package/build/core/run-control/runner-lease.js +172 -0
  165. package/build/core/run-control/sqlite-process-lifetime-lock.js +174 -0
  166. package/build/core/seal.js +44 -32
  167. package/build/core/settlement/claim-delivery.js +22 -9
  168. package/build/core/settlement/claim.js +8 -6
  169. package/build/core/settlement/petition-claim-gates.js +9 -8
  170. package/build/core/settlement/petition-head-guard.js +5 -6
  171. package/build/core/settlement/petition-preview.js +21 -5
  172. package/build/core/settlement/petition.js +51 -17
  173. package/build/core/settlement/settlement.js +20 -15
  174. package/build/core/settlement/verdict.js +2 -2
  175. package/build/core/settlement/verification.js +492 -364
  176. package/build/core/status/board.js +184 -75
  177. package/build/core/status/drift.js +8 -10
  178. package/build/core/task/board.js +1 -0
  179. package/build/core/task/commands.js +25 -0
  180. package/build/core/task/compose.js +328 -0
  181. package/build/core/task/document.js +102 -39
  182. package/build/core/task/index.js +4 -3
  183. package/build/core/task/settlement-git.js +70 -30
  184. package/build/core/task/settlement-policy.js +27 -14
  185. package/build/core/task/source-board.js +1 -0
  186. package/build/core/task/task-bind-preparation.js +59 -9
  187. package/build/core/task/task-contract.js +2 -2
  188. package/build/core/task/task-git-store.js +1 -0
  189. package/build/core/task/task-store-repository.js +2 -0
  190. package/build/core/task/task.js +2 -2
  191. package/build/core/transcripts.js +4 -4
  192. package/build/core/verification-declaration.js +95 -0
  193. package/build/core/worktree-bootstrap.js +1 -1
  194. package/build/core/worktree-path.js +7 -11
  195. package/build/flow-error.js +15 -2
  196. package/build/generated/version.js +2 -2
  197. package/build/git/branches.js +41 -70
  198. package/build/git/commits.js +92 -21
  199. package/build/git/core.js +9 -161
  200. package/build/git/diff/preview.js +4 -4
  201. package/build/git/diff/read.js +4 -4
  202. package/build/git/diff/structured.js +5 -5
  203. package/build/git/process.js +229 -0
  204. package/build/git/refs.js +30 -5
  205. package/build/git/staging.js +39 -115
  206. package/build/git/streaming-batch.js +9 -16
  207. package/build/git/worktree.js +104 -91
  208. package/build/index.js +7 -0
  209. package/package.json +2 -2
  210. package/skills/keiyaku-akuma/SKILL.md +27 -21
  211. package/skills/keiyaku-task/SKILL.md +61 -5
  212. package/skills/keiyaku-workflow/SKILL.md +81 -26
  213. package/build/cli/commands/projection/revive/meta.js +0 -12
  214. package/build/core/projection/projection-life-protocol.js +0 -115
  215. package/build/core/projection/projection-runner-lock.js +0 -382
  216. package/build/core/projection/tell/database.js +0 -127
  217. package/build/core/settlement/verification-coordination.js +0 -305
  218. package/build/core/settlement/verification-supervisor.js +0 -275
@@ -5,6 +5,12 @@ import { runGitProcess } from "../../git/core.js";
5
5
  import { openGitStreamingBatch } from "../../git/streaming-batch.js";
6
6
  import { parseTaskDocument } from "./task.js";
7
7
  import { parseTaskCoordinate } from "./coordinate.js";
8
+ function observePublicationEffect(observer, effect) {
9
+ try {
10
+ observer?.(effect);
11
+ }
12
+ catch { /* observation cannot participate in the Git transaction */ }
13
+ }
8
14
  async function gitResult(cwd, args, input, env, signal) {
9
15
  return await runGitProcess(cwd, args, { input, env, signal });
10
16
  }
@@ -78,9 +84,11 @@ export async function createTaskMutationCommit(input) {
78
84
  ], undefined, env, input.signal);
79
85
  }
80
86
  const tree = (await git(input.cwd, ["write-tree"], undefined, env, input.signal)).trim();
81
- const parentTree = (await git(input.cwd, ["show", "-s", "--format=%T", input.parent], undefined, undefined, input.signal)).trim();
82
- if (tree === parentTree)
83
- return input.parent;
87
+ if (!input.requireCommit) {
88
+ const parentTree = (await git(input.cwd, ["show", "-s", "--format=%T", input.parent], undefined, undefined, input.signal)).trim();
89
+ if (tree === parentTree)
90
+ return input.parent;
91
+ }
84
92
  return (await git(input.cwd, ["commit-tree", tree, "-p", input.parent, "-m", input.subject], undefined, undefined, input.signal)).trim();
85
93
  }
86
94
  finally {
@@ -88,15 +96,37 @@ export async function createTaskMutationCommit(input) {
88
96
  }
89
97
  }
90
98
  /** Stage only the mutated task paths into the real index; leave unrelated entries alone. */
91
- export async function reconcileTaskMutationIndex(cwd, writes, signal) {
99
+ async function reconcileTaskMutationIndexPath(cwd, write, signal) {
100
+ const oid = (await git(cwd, ["hash-object", "-w", "--stdin"], write.text, undefined, signal)).trim();
101
+ await git(cwd, [
102
+ "update-index",
103
+ "--add",
104
+ "--cacheinfo",
105
+ `100644,${oid},${taskMutationPath(write)}`,
106
+ ], undefined, undefined, signal);
107
+ }
108
+ export async function reconcileTaskMutationIndex(cwd, writes, signal, reconcilePath = async (input) => {
109
+ await reconcileTaskMutationIndexPath(input.cwd, input.write, input.signal);
110
+ }) {
92
111
  for (const write of writes) {
93
- const oid = (await git(cwd, ["hash-object", "-w", "--stdin"], write.text, undefined, signal)).trim();
94
- await git(cwd, [
95
- "update-index",
96
- "--add",
97
- "--cacheinfo",
98
- `100644,${oid},${taskMutationPath(write)}`,
99
- ], undefined, undefined, signal);
112
+ await reconcilePath({ cwd, write, signal });
113
+ }
114
+ }
115
+ async function snapshotTaskMutationIndex(cwd, writes, signal) {
116
+ const snapshot = [];
117
+ for (const write of writes) {
118
+ const taskPath = taskMutationPath(write);
119
+ const output = await git(cwd, ["ls-files", "--stage", "-z", "--", taskPath], undefined, undefined, signal);
120
+ snapshot.push({ path: taskPath, entries: output.split("\0").filter(Boolean) });
121
+ }
122
+ return snapshot;
123
+ }
124
+ async function restoreTaskMutationIndex(cwd, snapshot, signal) {
125
+ for (const item of snapshot) {
126
+ await git(cwd, ["update-index", "--force-remove", "--", item.path], undefined, undefined, signal);
127
+ if (item.entries.length > 0) {
128
+ await git(cwd, ["update-index", "-z", "--index-info"], `${item.entries.join("\0")}\0`, undefined, signal);
129
+ }
100
130
  }
101
131
  }
102
132
  /**
@@ -114,20 +144,33 @@ export async function publishTaskMutationCommit(input, deps = {}) {
114
144
  parent: branch.head,
115
145
  writes: input.writes,
116
146
  subject: input.subject,
147
+ requireCommit: input.requireCommit,
117
148
  signal: input.signal,
118
149
  });
119
150
  if (commit === branch.head) {
120
- input.observeEffect?.({ effect: "task-reconciliation", status: "succeeded", commit, detail: "current" });
151
+ const indexBefore = await snapshotTaskMutationIndex(input.cwd, input.writes, input.signal);
121
152
  try {
122
- await reconcileTaskMutationIndex(input.cwd, input.writes, input.signal);
123
- input.observeEffect?.({ effect: "task-index-reconciliation", status: "succeeded", commit });
153
+ await reconcileTaskMutationIndex(input.cwd, input.writes, input.signal, deps.reconcileIndexPath);
154
+ observePublicationEffect(input.observeEffect, { effect: "task-reconciliation", status: "succeeded", commit, detail: "current" });
155
+ observePublicationEffect(input.observeEffect, { effect: "task-index-reconciliation", status: "succeeded", commit });
156
+ return commit;
124
157
  }
125
158
  catch (error) {
126
- input.observeEffect?.({ effect: "task-index-reconciliation", status: "failed", commit, error });
159
+ try {
160
+ await restoreTaskMutationIndex(input.cwd, indexBefore, input.signal);
161
+ }
162
+ catch (restoreError) {
163
+ observePublicationEffect(input.observeEffect, { effect: "task-index-reconciliation", status: "failed", commit, error: restoreError });
164
+ throw new FlowError("INTERNAL_STATE", "cannot restore task index after reconciliation failure", { cause: restoreError });
165
+ }
166
+ observePublicationEffect(input.observeEffect, { effect: "task-index-reconciliation", status: "failed", commit, error });
167
+ throw error;
127
168
  }
128
- return commit;
129
169
  }
170
+ const indexBefore = await snapshotTaskMutationIndex(input.cwd, input.writes, input.signal);
130
171
  try {
172
+ await reconcileTaskMutationIndex(input.cwd, input.writes, input.signal, deps.reconcileIndexPath);
173
+ observePublicationEffect(input.observeEffect, { effect: "task-index-reconciliation", status: "succeeded", commit });
131
174
  await updateBranch({
132
175
  cwd: input.cwd,
133
176
  ref: branch.ref,
@@ -135,22 +178,22 @@ export async function publishTaskMutationCommit(input, deps = {}) {
135
178
  expected: branch.head,
136
179
  signal: input.signal,
137
180
  }, deps);
138
- input.observeEffect?.({ effect: "task-reconciliation", status: "succeeded", commit, detail: "published" });
139
- try {
140
- await reconcileTaskMutationIndex(input.cwd, input.writes, input.signal);
141
- input.observeEffect?.({ effect: "task-index-reconciliation", status: "succeeded", commit });
142
- }
143
- catch (error) {
144
- // Branch fact remains authoritative; index may be repaired by a later mutation.
145
- input.observeEffect?.({ effect: "task-index-reconciliation", status: "failed", commit, error });
146
- }
181
+ observePublicationEffect(input.observeEffect, { effect: "task-reconciliation", status: "succeeded", commit, detail: "published" });
147
182
  return commit;
148
183
  }
149
184
  catch (error) {
150
185
  lastError = error;
186
+ try {
187
+ await restoreTaskMutationIndex(input.cwd, indexBefore, input.signal);
188
+ }
189
+ catch (restoreError) {
190
+ observePublicationEffect(input.observeEffect, { effect: "task-index-reconciliation", status: "failed", commit, error: restoreError });
191
+ throw new FlowError("INTERNAL_STATE", "cannot restore task index after publication failure", { cause: restoreError });
192
+ }
193
+ observePublicationEffect(input.observeEffect, { effect: "task-index-reconciliation", status: "failed", commit, error });
151
194
  }
152
195
  }
153
- input.observeEffect?.({ effect: "task-reconciliation", status: "failed", error: lastError });
196
+ observePublicationEffect(input.observeEffect, { effect: "task-reconciliation", status: "failed", error: lastError });
154
197
  throw lastError;
155
198
  }
156
199
  async function taskTreeEntries(cwd, commit) {
@@ -235,10 +278,7 @@ async function readTaskAdmissionEvidence(input, readDocument = taskDocumentAtCom
235
278
  const needs = [];
236
279
  for (const need of document.needs) {
237
280
  const prerequisite = await read(need);
238
- if (prerequisite.state !== "done" && prerequisite.state !== "drop") {
239
- throw new FlowError("INTERNAL_STATE", `bind base ${input.base} task ${taskId} has unsatisfied need ${need}`);
240
- }
241
- needs.push({ taskId: need, state: prerequisite.state });
281
+ needs.push({ taskId: need, state: prerequisite.state, ...(prerequisite.contractId === undefined ? {} : { contractId: prerequisite.contractId }) });
242
282
  }
243
283
  selected.push({ taskId, needs });
244
284
  }
@@ -1,5 +1,8 @@
1
1
  import { FlowError } from "../../flow-error.js";
2
2
  import { resolveTaskReadId } from "./board.js";
3
+ import { openTaskStore } from "./task-git-store.js";
4
+ import { taskRepositoryFromStore } from "./task-store-repository.js";
5
+ import { readTaskBoard } from "./commands.js";
3
6
  import { advancedTaskUpdatedAt } from "./model.js";
4
7
  export function requireHealthySettlementBoard(board) {
5
8
  if (board.diseases.length > 0) {
@@ -20,6 +23,7 @@ function settlementDocument(task, contractId, updatedAt) {
20
23
  from: [...task.from],
21
24
  supersedes: [...task.supersedes],
22
25
  relatesTo: [...task.relatesTo],
26
+ notes: task.notes.map((note) => ({ ...note })),
23
27
  createdAt: task.createdAt,
24
28
  updatedAt: advancedTaskUpdatedAt(task, updatedAt),
25
29
  creator: task.creator,
@@ -31,6 +35,12 @@ function settlementDocument(task, contractId, updatedAt) {
31
35
  export function contractPointerDocument(task, contractId, updatedAt) {
32
36
  return settlementDocument(task, contractId, updatedAt);
33
37
  }
38
+ export function boundTaskDocument(task, contractId, updatedAt) {
39
+ const document = settlementDocument(task, contractId, updatedAt);
40
+ document.state = "open";
41
+ delete document.startedAt;
42
+ return document;
43
+ }
34
44
  export function reopenedTaskDocument(task, updatedAt) {
35
45
  const document = settlementDocument(task, undefined, updatedAt);
36
46
  document.state = "open";
@@ -38,25 +48,28 @@ export function reopenedTaskDocument(task, updatedAt) {
38
48
  return document;
39
49
  }
40
50
  function validateReadyForBind(board, task) {
41
- if (task.state === "in_progress") {
42
- throw new FlowError("INVALID_TARGET", `task ${task.coordinate} is in_progress; stop it before bind`);
43
- }
44
- if (task.state !== "open") {
45
- throw new FlowError("INVALID_TARGET", `task ${task.coordinate} is ${task.state}, not ready for bind`);
46
- }
47
- const unsatisfied = task.needs
48
- .filter((need) => {
49
- const dependency = board.byId.get(need);
50
- return dependency.state === "open" || dependency.state === "in_progress" || dependency.state === "on_hold";
51
- })
52
- .sort();
53
- if (unsatisfied.length > 0) {
54
- throw new FlowError("INVALID_TARGET", `task ${task.coordinate} is blocked by: ${unsatisfied.join(", ")}`);
51
+ if (task.state !== "open" && task.state !== "in_progress") {
52
+ throw new FlowError("INVALID_TARGET", `task ${task.coordinate} is ${task.state}, not open or in_progress for bind`);
55
53
  }
56
54
  if (task.contractId !== undefined) {
57
55
  throw new FlowError("INVALID_TARGET", `task ${task.coordinate} is already bound to contract ${task.contractId}`);
58
56
  }
59
57
  }
58
+ /** Read the sole live task prerequisite gate for a task-selected contract. */
59
+ export async function readRemainingTaskNeedsForSettlement(cwd, evidence) {
60
+ const needIds = [...new Set(evidence?.selected.flatMap((task) => task.needs
61
+ .filter((need) => need.contractId === undefined)
62
+ .map((need) => need.taskId)) ?? [])].sort();
63
+ if (needIds.length === 0)
64
+ return [];
65
+ const board = requireHealthySettlementBoard(await readTaskBoard(taskRepositoryFromStore(await openTaskStore(cwd))));
66
+ return needIds.map((id) => {
67
+ const task = board.byId.get(id);
68
+ if (!task)
69
+ throw new FlowError("INTERNAL_STATE", `task prerequisite ${id} disappeared from the authoritative board`);
70
+ return { id, satisfied: task.state === "done" || task.state === "drop", state: task.state };
71
+ });
72
+ }
60
73
  export function selectReadyTasksForBind(board, taskIds) {
61
74
  requireHealthySettlementBoard(board);
62
75
  const rootIds = taskIds.map((taskId) => resolveTaskReadId(board, taskId));
@@ -24,6 +24,7 @@ function taskDocument(parsed) {
24
24
  from: [...parsed.from],
25
25
  supersedes: [...parsed.supersedes],
26
26
  relatesTo: [...parsed.relatesTo],
27
+ notes: parsed.notes.map((note) => ({ ...note })),
27
28
  createdAt: parsed.createdAt,
28
29
  ...(parsed.updatedAt === undefined ? {} : { updatedAt: parsed.updatedAt }),
29
30
  creator: parsed.creator,
@@ -1,4 +1,5 @@
1
1
  import { FlowError } from "../../flow-error.js";
2
+ import { readCommitCoordinate } from "../../git/commits.js";
2
3
  import { runGitProcess } from "../../git/core.js";
3
4
  import { assertGitObservation, requireGitRepository } from "../../git/branches.js";
4
5
  import { inspectCurrentBranch, createTaskBoardCommit, publishForwardTaskBoardCommit, reconcileTaskPathsetIndex, refHead, taskSourcesAtCommit, taskWritesFromSources, } from "./settlement-git.js";
@@ -21,6 +22,7 @@ function linkedTaskDocument(task, contractId, at) {
21
22
  from: [...task.from],
22
23
  supersedes: [...task.supersedes],
23
24
  relatesTo: [...task.relatesTo],
25
+ notes: task.notes.map((note) => ({ ...note })),
24
26
  createdAt: task.createdAt,
25
27
  updatedAt: advancedTaskUpdatedAt(task, at),
26
28
  creator: task.creator,
@@ -32,11 +34,10 @@ function selectedEvidence(board, selected) {
32
34
  return selected.map((task) => ({
33
35
  taskId: task.coordinate,
34
36
  needs: task.needs.map((taskId) => {
35
- const state = board.byId.get(taskId)?.state;
36
- if (state !== "done" && state !== "drop") {
37
- throw new FlowError("INTERNAL_STATE", `ready task ${task.coordinate} has unsatisfied need ${taskId}`);
38
- }
39
- return { taskId, state };
37
+ const need = board.byId.get(taskId);
38
+ if (!need)
39
+ throw new FlowError("INTERNAL_STATE", `selected task ${task.coordinate} has missing need ${taskId}`);
40
+ return { taskId, state: need.state, ...(need.contractId === undefined ? {} : { contractId: need.contractId }) };
40
41
  }),
41
42
  }));
42
43
  }
@@ -100,6 +101,52 @@ export async function withPreparedTaskBind(input, deps = {}) {
100
101
  return { preparation, result };
101
102
  }, { signal: input.signal });
102
103
  }
104
+ /** Task-lock admission from bind's already-observed repository and board coordinates. */
105
+ export async function withPreparedTaskBindSnapshot(input, deps = {}) {
106
+ return await withLockHeldTaskBoard(input.location.root, async (transaction) => {
107
+ const prepared = await prepareLockedTaskBindFromSnapshot(transaction, input, deps);
108
+ return { preparation: prepared, result: await input.transact(prepared, transaction) };
109
+ }, { signal: input.signal }, input.location);
110
+ }
111
+ async function prepareLockedTaskBindFromSnapshot(transaction, input, deps) {
112
+ if (transaction.location.mode !== "git")
113
+ throw new FlowError("NOT_GIT_REPO", "task bind requires Git task storage");
114
+ const observedBoard = await input.observeBoard();
115
+ const board = requireHealthySettlementBoard(taskBoardFromSourceBoard(buildTaskSourceBoard(transaction.sources)));
116
+ const selected = selectReadyTasksForBind(board, input.taskIds);
117
+ if (selected.length === 0)
118
+ throw new FlowError("INVALID_TARGET", "task bind requires at least one selected task");
119
+ const attempt = await input.prepare(selected);
120
+ const evidence = selectedEvidence(board, selected);
121
+ const linked = prepareTaskDocumentReplacements(transaction.sources, selected.map((task) => linkedTaskDocument(task, attempt.contractId, attempt.at)));
122
+ const linkedIds = new Set(selected.map((task) => task.coordinate));
123
+ const linkedSources = linked.sources.filter((source) => linkedIds.has(taskCoordinateFromSourcePath(source.path).coordinate));
124
+ const originalSources = transaction.sources.filter((source) => linkedIds.has(taskCoordinateFromSourcePath(source.path).coordinate));
125
+ const newHead = await (deps.createTaskBoardCommit ?? createTaskBoardCommit)({
126
+ cwd: transaction.location.root,
127
+ gitDir: transaction.location.gitDir,
128
+ root: transaction.location.root,
129
+ parent: observedBoard.head,
130
+ sources: linkedSources,
131
+ subject: selected.length === 1
132
+ ? `Bind task ${selected[0].coordinate} to contract ${attempt.contractId}`
133
+ : `Bind ${selected.length} tasks to contract ${attempt.contractId}`,
134
+ });
135
+ return {
136
+ boardRef: observedBoard.ref,
137
+ expectedHead: observedBoard.head,
138
+ newHead,
139
+ entryBase: newHead,
140
+ taskIds: selected.map((task) => task.coordinate),
141
+ selected: evidence,
142
+ taskBoard: { ref: observedBoard.ref, expectedHead: observedBoard.head, head: newHead },
143
+ replay: {
144
+ paths: selected.map((task) => `.keiyaku/tasks/${task.coordinate}.md`).sort(),
145
+ linkedSources,
146
+ originalSources,
147
+ },
148
+ };
149
+ }
103
150
  /** Complete the prepared task projection while its authority transaction still owns the task lock. */
104
151
  export async function replayPreparedTaskBindLocked(transaction, prepared) {
105
152
  if (transaction.location.mode !== "git")
@@ -149,11 +196,14 @@ export async function replayCommittedTaskBindForfeit(cwd, commit, boardRef, task
149
196
  return await withLockHeldTaskBoard(cwd, async (transaction) => {
150
197
  if (transaction.location.mode !== "git")
151
198
  throw new FlowError("NOT_GIT_REPO", "task forfeit replay requires Git task storage");
152
- const parentResult = await runGitProcess(transaction.location.root, ["rev-parse", "--verify", `${commit}^`]);
153
- if (parentResult.status !== 0 || parentResult.error) {
154
- throw new FlowError("INTERNAL_STATE", `committed task bind base ${commit} has no readable parent`, { cause: parentResult.error });
199
+ const coordinate = await readCommitCoordinate(transaction.location.root, commit);
200
+ if (coordinate.status !== "ok") {
201
+ throw new FlowError("INTERNAL_STATE", `committed task bind base ${commit} has no readable parent`, { cause: coordinate.status === "failed" ? coordinate.error : undefined });
202
+ }
203
+ const parent = coordinate.coordinate.parents[0];
204
+ if (!parent) {
205
+ throw new FlowError("INTERNAL_STATE", `committed task bind base ${commit} has no readable parent`);
155
206
  }
156
- const parent = parentResult.stdout.trim();
157
207
  const paths = taskIds.map((taskId) => `.keiyaku/tasks/${taskId}.md`).sort();
158
208
  const originalSources = await taskSourcesAtCommit(transaction.location.root, transaction.location.root, parent, paths);
159
209
  if (!originalSources)
@@ -5,7 +5,7 @@ import { prepareTaskDocumentReplacements, taskBoardFromSourceBoard, taskReposito
5
5
  import { buildTaskSourceBoard } from "./source-board.js";
6
6
  import { formatTaskCoordinate } from "./coordinate.js";
7
7
  import { createTaskBoardCommit, publishTaskMutationCommit, inspectCurrentBranch, publishForwardTaskBoardCommit, reconcileTaskPathsetIndex, refHead, taskContractAtCommit, taskSourcesAtCommit, taskWritesFromSources, trackedTaskPaths, updateBranch, } from "./settlement-git.js";
8
- import { contractPointerDocument, doneTaskReplacements, reopenedTaskDocument, requireHealthySettlementBoard, requireSettledTask, selectReadyTasksForBind, tasksForContract, } from "./settlement-policy.js";
8
+ import { boundTaskDocument, contractPointerDocument, doneTaskReplacements, reopenedTaskDocument, requireHealthySettlementBoard, requireSettledTask, selectReadyTasksForBind, tasksForContract, } from "./settlement-policy.js";
9
9
  import { stableRepoRoot } from "../worktree-path.js";
10
10
  /**
11
11
  * Read the same healthy task board and run the same ready-task selector as bind
@@ -147,7 +147,7 @@ export async function settleTaskBoardForBind(input, deps = {}) {
147
147
  const prepared = taskBound ? await input.prepare(selectedTasks) : input;
148
148
  const contractId = prepared.contractId;
149
149
  if (taskBound && selectedTasks.length > 0) {
150
- await replaceTaskDocuments(repository, selectedTasks.map((task) => contractPointerDocument(task, contractId, updatedAt)));
150
+ await replaceTaskDocuments(repository, selectedTasks.map((task) => boundTaskDocument(task, contractId, updatedAt)));
151
151
  }
152
152
  const taskIds = taskBound ? selectedTasks.map((t) => t.coordinate) : undefined;
153
153
  const selectedPaths = ownedTaskPaths(await repository.read(), taskIds);
@@ -337,6 +337,7 @@ export function openTaskStoreAt(board, deps = {}) {
337
337
  gitDir: transaction.location.gitDir,
338
338
  writes: plan.writes,
339
339
  subject: subject,
340
+ requireCommit: plan.requireCommit,
340
341
  signal: deps.signal,
341
342
  });
342
343
  }
@@ -15,6 +15,7 @@ export function taskBoardFromSourceBoard(sourceBoard) {
15
15
  from: [...task.from],
16
16
  supersedes: [...task.supersedes],
17
17
  relatesTo: [...task.relatesTo],
18
+ notes: task.notes.map((note) => ({ ...note })),
18
19
  createdAt: task.createdAt,
19
20
  ...(task.updatedAt === undefined ? {} : { updatedAt: task.updatedAt }),
20
21
  creator: task.creator,
@@ -75,6 +76,7 @@ function mutationPlan(sources, mutation) {
75
76
  writes: prepared.writes,
76
77
  value: mutation.select(prepared.board),
77
78
  ...(mutation.subject === undefined ? {} : { subject: mutation.subject }),
79
+ requireCommit: mutation.requireCommit ?? true,
78
80
  };
79
81
  }
80
82
  export function taskRepositoryFromStore(store) {
@@ -1,7 +1,7 @@
1
1
  export { deriveTaskDisplay, parseTaskFilter, parseTaskSort, selectTaskList } from "./query.js";
2
2
  export { advancedTaskUpdatedAt, taskUpdatedAt } from "./model.js";
3
- export { parseTaskDocument, serializeTaskDocument, } from "./document.js";
3
+ export { parseTaskCreationDocument, parseTaskDocument, serializeTaskDocument, } from "./document.js";
4
4
  export { resolveTaskReadId, } from "./board.js";
5
5
  export { buildTaskBoard } from "./source-board.js";
6
6
  export { buildTaskTree } from "./tree.js";
7
- export { readTaskBoard, createTask, updateTask, updateTaskWithReceipt, startTask, stopTask, holdTask, resumeTask, doneTask, dropTask, selectSoleInProgress, rollbackOrphanTaskPointers, } from "./commands.js";
7
+ export { readTaskBoard, createTask, updateTask, updateTaskWithReceipt, startTask, stopTask, holdTask, resumeTask, doneTask, dropTask, dropTaskWithNote, appendTaskNote, selectSoleInProgress, rollbackOrphanTaskPointers, } from "./commands.js";
@@ -410,7 +410,7 @@ export async function locateResponseArtifact(input) {
410
410
  return carry(error);
411
411
  }
412
412
  if (!isResponseArtifactId(identity)) {
413
- return carry(new FlowError("INVALID_RESPONSE_PATH", `${identity} is not a response artifact handle; revive expects an artifact id`));
413
+ return carry(new FlowError("INVALID_RESPONSE_PATH", `${identity} is not a response artifact handle; fork expects an artifact id`));
414
414
  }
415
415
  let physicalLocalRoot;
416
416
  try {
@@ -463,11 +463,11 @@ export async function locateResponseArtifact(input) {
463
463
  }
464
464
  export function requireResumableArtifact(artifact) {
465
465
  if (!artifact.session)
466
- throw artifactError(artifact.artifactId, RESPONSE_HISTORY_SESSION_KEY, "is missing; this conversation cannot be revived");
466
+ throw artifactError(artifact.artifactId, RESPONSE_HISTORY_SESSION_KEY, "is missing; this conversation cannot be forked");
467
467
  if (!artifact.subagentName)
468
- throw artifactError(artifact.artifactId, "subagent_name", "is missing; this conversation cannot be revived");
468
+ throw artifactError(artifact.artifactId, "subagent_name", "is missing; this conversation cannot be forked");
469
469
  if (!artifact.launchSnapshot)
470
- throw artifactError(artifact.artifactId, RESPONSE_HISTORY_LAUNCH_SNAPSHOT_KEY, "is missing; this conversation cannot be revived");
470
+ throw artifactError(artifact.artifactId, RESPONSE_HISTORY_LAUNCH_SNAPSHOT_KEY, "is missing; this conversation cannot be forked");
471
471
  return artifact;
472
472
  }
473
473
  export async function nextReviewResponseNumber(cwd) {
@@ -0,0 +1,95 @@
1
+ import { FlowError } from "../flow-error.js";
2
+ import { z } from "zod";
3
+ export const VERIFICATION_EXECUTORS = ["bash", "zsh", "pwsh"];
4
+ export const verificationDeclarationSchema = z.object({
5
+ executor: z.enum(VERIFICATION_EXECUTORS),
6
+ script: z.string().refine((value) => value.trim().length > 0, "expected a nonblank script"),
7
+ }).strict();
8
+ function example(heading) {
9
+ return `${heading}\n\`\`\`bash\n<verification script>\n\`\`\``;
10
+ }
11
+ function invalid(options, detail) {
12
+ throw new FlowError("INVALID_KEIYAKU_DRAFT", `${options.heading} ${detail}. Use a fenced executor script block:\n${example(options.heading)}`);
13
+ }
14
+ function isExecutor(value) {
15
+ return VERIFICATION_EXECUTORS.includes(value);
16
+ }
17
+ /**
18
+ * Parse the sole persisted contract Verification declaration grammar. Bodies
19
+ * are deliberately source-level so shell backticks and blank lines stay exact.
20
+ */
21
+ export function parseVerificationDeclarations(source, options) {
22
+ const lines = source.split(/\r?\n/);
23
+ const declarations = [];
24
+ let sawNone = false;
25
+ for (let index = 0; index < lines.length;) {
26
+ const line = lines[index];
27
+ if (line.trim().length === 0) {
28
+ index += 1;
29
+ continue;
30
+ }
31
+ if (options.allowNone && line.trim() === "- (none)") {
32
+ if (declarations.length > 0 || sawNone) {
33
+ invalid(options, "the explicit '- (none)' replacement cannot be combined with Verification declarations");
34
+ }
35
+ sawNone = true;
36
+ index += 1;
37
+ continue;
38
+ }
39
+ const opening = line.match(/^ {0,3}(`{3,}|~{3,})(.*)$/);
40
+ if (!opening)
41
+ invalid(options, "must contain only direct fenced script blocks");
42
+ const fence = opening[1];
43
+ const delimiter = fence[0];
44
+ const executor = opening[2].trim();
45
+ if (!executor)
46
+ invalid(options, "fenced script blocks require an executor header (bash, zsh, or pwsh)");
47
+ if (!isExecutor(executor))
48
+ invalid(options, `does not support the '${executor}' executor`);
49
+ const body = [];
50
+ index += 1;
51
+ let closed = false;
52
+ while (index < lines.length) {
53
+ const candidate = lines[index];
54
+ const close = candidate.match(/^ {0,3}(`+|~+)[ \t]*$/);
55
+ if (close && close[1][0] === delimiter && close[1].length >= fence.length) {
56
+ closed = true;
57
+ index += 1;
58
+ break;
59
+ }
60
+ body.push(candidate);
61
+ index += 1;
62
+ }
63
+ if (!closed)
64
+ invalid(options, "contains an unclosed fenced script block");
65
+ const script = body.join("\n");
66
+ if (script.trim().length === 0)
67
+ invalid(options, "contains an empty fenced script block");
68
+ if (sawNone) {
69
+ invalid(options, "the explicit '- (none)' replacement cannot be combined with Verification declarations");
70
+ }
71
+ declarations.push({ executor, script });
72
+ }
73
+ if (sawNone)
74
+ return [];
75
+ if (declarations.length === 0)
76
+ invalid(options, "must contain at least one fenced script block");
77
+ return declarations;
78
+ }
79
+ function fenceFor(executor, script) {
80
+ const candidateLengths = ["`", "~"].map((delimiter) => {
81
+ const largestRun = script.split("\n").reduce((largest, line) => {
82
+ const match = line.match(new RegExp(`^ {0,3}\\${delimiter}+`));
83
+ return Math.max(largest, match?.[0].trim().length ?? 0);
84
+ }, 0);
85
+ return { delimiter, length: Math.max(3, largestRun + 1) };
86
+ });
87
+ const selected = candidateLengths.sort((left, right) => left.length - right.length)[0];
88
+ return selected.delimiter.repeat(selected.length);
89
+ }
90
+ export function renderVerificationDeclarations(declarations) {
91
+ return declarations.map((declaration) => {
92
+ const fence = fenceFor(declaration.executor, declaration.script);
93
+ return `${fence}${declaration.executor}\n${declaration.script}\n${fence}`;
94
+ }).join("\n\n");
95
+ }
@@ -1,5 +1,5 @@
1
1
  import { spawn as nodeSpawn } from "node:child_process";
2
- import { terminateProcessGroup } from "./process-group.js";
2
+ import { terminateProcessGroup } from "./run-control/process-tree.js";
3
3
  export const WORKTREE_BOOTSTRAP_OUTPUT_TAIL_BYTES = 2048;
4
4
  export const WORKTREE_BOOTSTRAP_KILL_GRACE_MS = 10_000;
5
5
  function defaultWait(ms) {
@@ -101,23 +101,19 @@ export function worktreeContainsCoordinate(root, candidate) {
101
101
  }
102
102
  export async function stableRepoRoot(cwd) {
103
103
  const git = createGit(cwd);
104
- let bare;
104
+ let facts;
105
105
  try {
106
- bare = (await git.raw(["rev-parse", "--is-bare-repository"])).trim();
106
+ facts = (await git.raw(["rev-parse", "--is-bare-repository", "--path-format=absolute", "--git-common-dir"])).trim().split("\n");
107
107
  }
108
108
  catch (error) {
109
- throw wrapGitError("rev-parse --is-bare-repository", error, cwd);
109
+ throw wrapGitError("rev-parse repository coordinate", error, cwd);
110
110
  }
111
+ const [bare, commonDir] = facts;
111
112
  if (bare === "true") {
112
113
  throw new FlowError("INTERNAL_STATE", "Keiyaku worktree operations require a non-bare repository");
113
114
  }
114
- let commonDir;
115
- try {
116
- commonDir = (await git.raw(["rev-parse", "--path-format=absolute", "--git-common-dir"])).trim();
117
- }
118
- catch (error) {
119
- throw wrapGitError("rev-parse --path-format=absolute --git-common-dir", error, cwd);
120
- }
115
+ if (!commonDir)
116
+ throw new FlowError("INTERNAL_STATE", "Git did not report its common directory");
121
117
  const stableRoot = gitCommonRootPath(commonDir);
122
118
  if (!path.isAbsolute(stableRoot)) {
123
119
  throw new FlowError("INTERNAL_STATE", `git common directory is not absolute: ${commonDir}`);
@@ -132,7 +128,7 @@ export async function stableRepoRoot(cwd) {
132
128
  export async function contractWorktreePathFromBind(cwd, contractId, bind) {
133
129
  return contractWorktreePathFromStableRoot(await stableRepoRoot(cwd), contractId, bind);
134
130
  }
135
- function contractWorktreePathFromStableRoot(stableRoot, contractId, bind) {
131
+ export function contractWorktreePathFromStableRoot(stableRoot, contractId, bind) {
136
132
  return path.join(stableRoot, ".keiyaku", "wt", bind?.data.place ?? contractId);
137
133
  }
138
134
  export async function contractWorktreePath(cwd, contractId) {
@@ -55,6 +55,12 @@ export function resolveFlowErrorHints(code, context, facts) {
55
55
  if (facts?.kind === "projection_wake_failure") {
56
56
  return [];
57
57
  }
58
+ if (facts?.kind === "task_contract_workspace") {
59
+ return ["Select an active contract with a materialized workspace, or retry from the desired checkout with -C."];
60
+ }
61
+ if (facts?.kind === "carrier_dispatch_refusal") {
62
+ return [facts.recoveryCommand];
63
+ }
58
64
  switch (code) {
59
65
  case "NOT_GIT_REPO":
60
66
  return ["The provided `cwd` is not a git repository."];
@@ -64,10 +70,17 @@ export function resolveFlowErrorHints(code, context, facts) {
64
70
  return [`Next: open an arc with \`${context.arcToolName}\`, then use \`${context.renewToolName}\` when delivery must follow the current base, or \`${context.closeToolName}\` to settle it.`];
65
71
  case "CONTRACT_AMBIGUOUS":
66
72
  return ["Address a specific contract workspace, or use `call --bare` to run without contract attachment."];
73
+ case "CONTRACT_WORKSPACE_UNAVAILABLE":
74
+ return [];
67
75
  case "TASK_AMBIGUOUS":
68
76
  return ["Pass one complete task coordinate from the listed candidates."];
69
77
  case "UNKNOWN_COMMAND":
70
78
  return ["Run `keiyaku --help` to inspect available commands."];
79
+ case "UNKNOWN_OPTION":
80
+ case "UNKNOWN_ADDRESS":
81
+ case "INVALID_USAGE":
82
+ case "STDIN_EMPTY":
83
+ return [];
71
84
  case "EMPTY_PARAM":
72
85
  return ["One or more required parameters are empty."];
73
86
  case "DIRTY_WORKTREE":
@@ -95,7 +108,7 @@ export function resolveFlowErrorHints(code, context, facts) {
95
108
  case "FROM_DRAFT_NOT_FOUND":
96
109
  return ["The path in `from_draft` does not exist. `from_draft` must reference an existing file path (absolute or relative to `cwd`)."];
97
110
  case "INVALID_KEIYAKU_DRAFT":
98
- return ["Invalid draft/input. Use a `# <title>` heading with non-empty `## Context`, `## Objective`, `## Design`, `## Scope`, `## Criteria`, and `## Verification` sections (or the equivalent shape in `from_draft`)."];
111
+ return ["Invalid draft/input. Use a `# <title>` heading with non-empty `## Context`, `## Objective`, `## Design & Approach`, `## Scope`, `## Criteria`, and `## Verification` sections (or the equivalent shape in `from_draft`)."];
99
112
  case "BIND_TASK_INCOMPLETE":
100
113
  return ["Task supplies title/objective defaults; pipe only the missing sections as partial contract Markdown on stdin."];
101
114
  case "KEIYAKU_FILE_EXISTS":
@@ -103,7 +116,7 @@ export function resolveFlowErrorHints(code, context, facts) {
103
116
  case "DETACHED_HEAD":
104
117
  return ["Repository is in detached HEAD state. Address an active contract workspace before retrying."];
105
118
  case "INVALID_RESPONSE_PATH":
106
- return ["`keiyaku revive ARTIFACT_ID` needs a valid previous Keiyaku response artifact that includes complete provenance plus `session` and `subagent_name`."];
119
+ return ["`keiyaku fork ARTIFACT_ID` needs a valid previous Keiyaku response artifact that includes complete provenance plus `session` and `subagent_name`."];
107
120
  case "INVALID_CLOSE_INTENT":
108
121
  return ["Invalid close intent. Valid values are `CLAIM` and `FORFEIT`."];
109
122
  case "INVALID_SETTINGS":
@@ -1,3 +1,3 @@
1
1
  // Generated into build output by scripts/build.mjs
2
- export const VERSION = "2.9.11";
3
- export const GIT_HASH = "4384d75";
2
+ export const VERSION = "2.9.13";
3
+ export const GIT_HASH = "0971934";