@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
@@ -2,9 +2,6 @@ import { formatTimelineDuration, renderPendingProjectionTell, renderProjectionAc
2
2
  import { displayColumns, resolveLineColumns, truncateColumns } from "./line-width.js";
3
3
  import { coreTextResponse, formatTellCommand, projectionRejoinHintLines, textResponse } from "./shared.js";
4
4
  import { renderTerminalFailureFace } from "./terminal-failure.js";
5
- function shortAddress(projectionId) {
6
- return /\/([0-9a-f]{8})$/.exec(projectionId)?.[1] ?? projectionId;
7
- }
8
5
  function phaseState(phase) {
9
6
  switch (phase) {
10
7
  case "done": return "returned";
@@ -34,16 +31,17 @@ function projectionIdentityParts(projectionId) {
34
31
  const match = /^(.*)\/([0-9a-f]{8})$/.exec(projectionId);
35
32
  return match ? { slug: match[1], shortId: match[2] } : { slug: "", shortId: projectionId };
36
33
  }
37
- function waitHeader(projectionId, akuma, snapshot, maxColumns) {
34
+ function waitHeader(projectionId, akuma, snapshot, maxColumns, alias) {
38
35
  const state = waitStateText(snapshot);
39
36
  const identity = projectionIdentityParts(projectionId);
40
37
  // Slug often mirrors the Akuma name; print once when they are the same fact.
41
38
  const description = identity.slug && akuma && identity.slug === akuma
42
39
  ? identity.slug
43
40
  : [identity.slug, akuma].filter(Boolean).join(" · ");
44
- const descriptionBudget = Math.max(0, maxColumns - displayColumns(`${identity.shortId} · ─ ${state}`));
41
+ const descriptionBudget = Math.max(0, maxColumns - displayColumns(`${projectionId}${alias ? ` (${alias})` : ""} · ─ ${state}`));
45
42
  const visibleDescription = truncateColumns(description, descriptionBudget);
46
- const prefix = visibleDescription ? `${identity.shortId} · ${visibleDescription}` : identity.shortId;
43
+ const canonical = `${projectionId}${alias ? ` (${alias})` : ""}`;
44
+ const prefix = visibleDescription ? `${canonical} · ${visibleDescription}` : canonical;
47
45
  const dashCount = Math.max(1, maxColumns - displayColumns(`${prefix} ${state}`));
48
46
  return `${prefix} ${"─".repeat(dashCount)} ${state}`;
49
47
  }
@@ -70,16 +68,16 @@ function ledgerHint(projectionId, snapshot) {
70
68
  const file = snapshot.eventLedgerFile ?? "events.jsonl";
71
69
  return `· ledger: .keiyaku/projection/${projectionId}/${file}`;
72
70
  }
73
- function recordReviveLines(artifactId) {
71
+ function recordForkLines(artifactId) {
74
72
  return [
75
73
  `record: ${artifactId}`,
76
- "revive starts a new projection from the record:",
77
- ` » keiyaku revive ${artifactId}`,
74
+ "fork starts a new projection from the record:",
75
+ ` » keiyaku fork ${artifactId}`,
78
76
  ];
79
77
  }
80
78
  function nextActionLines(projectionId, result, state) {
81
79
  const snapshot = result.snapshot;
82
- const address = shortAddress(projectionId);
80
+ const address = projectionId;
83
81
  if (state === "running") {
84
82
  return [...projectionRejoinHintLines(address)];
85
83
  }
@@ -101,11 +99,11 @@ function nextActionLines(projectionId, result, state) {
101
99
  return snapshot.artifactId
102
100
  ? [
103
101
  "The projection is lost — no terminal truth exists.",
104
- ...recordReviveLines(snapshot.artifactId),
102
+ ...recordForkLines(snapshot.artifactId),
105
103
  ]
106
104
  : [
107
105
  "The projection is lost — no terminal truth exists.",
108
- "no record survived — there is nothing to revive.",
106
+ "no record survived — there is nothing to fork.",
109
107
  ];
110
108
  }
111
109
  if (state === "stalled" || state === "unknown") {
@@ -166,18 +164,27 @@ function identityUnavailableResponse(projectionId, diagnostic) {
166
164
  exitCode: 1,
167
165
  };
168
166
  }
169
- function returnedResponse(projectionId, result) {
170
- // Successful terminal wait is response-only: no header, activity rows,
171
- // diagnostics, or rollup. Typed outcomeText is the first byte when present.
167
+ function returnedResponse(projectionId, result, alias) {
168
+ const identity = `${projectionId}${alias ? ` (${alias})` : ""}`;
172
169
  const nextActions = nextActionLines(projectionId, result, "returned");
173
170
  if (result.state === "terminal" && result.outcomeText) {
174
- return textResponse([result.outcomeText, "", ...nextActions].join("\n"));
171
+ return textResponse([identity, `answer: ${result.outcomeText}`, ...nextActions].join("\n"));
175
172
  }
176
- return textResponse(nextActions.join("\n"));
173
+ return textResponse([identity, ...nextActions].join("\n"));
177
174
  }
178
175
  export function buildWaitResponse(projectionId, result, options = {}) {
176
+ const payload = {
177
+ canonicalId: projectionId,
178
+ ...(options.inputToken ? { inputToken: options.inputToken } : {}),
179
+ ...(options.alias ? { alias: options.alias } : {}),
180
+ result,
181
+ };
182
+ const withPayload = (response) => ({
183
+ ...response,
184
+ payload: payload,
185
+ });
179
186
  if (result.state === "unknown") {
180
- return identityUnavailableResponse(projectionId, result.diagnostic);
187
+ return withPayload({ ...identityUnavailableResponse(projectionId, result.diagnostic), exitCode: 4 });
181
188
  }
182
189
  // A plural deadline may observe a verdict whose timestamp is at or after
183
190
  // the cutoff. The typed timeout result owns presentation in that case; the
@@ -188,20 +195,23 @@ export function buildWaitResponse(projectionId, result, options = {}) {
188
195
  : result.snapshot;
189
196
  const maxColumns = Math.min(120, options.maxColumns ?? resolveLineColumns());
190
197
  if (result.state === "terminal" && result.verdict.state === "killed") {
191
- return {
198
+ return withPayload({
192
199
  ...textResponse(`× killed ${projectionId}`),
193
200
  exitCode: 1,
194
- };
201
+ });
195
202
  }
196
203
  if (snapshot.phase === "startup-timeout") {
197
- return startupTimeoutResponse(projectionId, result.pact.akuma);
204
+ return withPayload(startupTimeoutResponse(projectionId, result.pact.akuma));
198
205
  }
199
206
  if (snapshot.phase === "unknown") {
200
- return unknownResponse(projectionId, result.pact.akuma, snapshot, maxColumns);
207
+ const response = unknownResponse(projectionId, result.pact.akuma, snapshot, maxColumns);
208
+ return result.state === "timeout"
209
+ ? withPayload({ ...response, exitCode: 124 })
210
+ : withPayload({ ...response, exitCode: 4 });
201
211
  }
202
212
  const state = phaseState(snapshot.phase);
203
213
  if (state === "returned") {
204
- return returnedResponse(projectionId, result);
214
+ return withPayload(returnedResponse(projectionId, result, options.alias));
205
215
  }
206
216
  const rendered = renderProjectionActivity(snapshot.activity, {
207
217
  anchorMs: snapshot.observedAtMs,
@@ -225,21 +235,26 @@ export function buildWaitResponse(projectionId, result, options = {}) {
225
235
  ...pendingTellRows(snapshot, maxColumns, activityRows.length),
226
236
  ]).map((row) => truncateColumns(row.line, maxColumns));
227
237
  const lines = [
228
- waitHeader(projectionId, result.pact.akuma, snapshot, maxColumns),
238
+ waitHeader(projectionId, result.pact.akuma, snapshot, maxColumns, options.alias),
229
239
  ...diagnosticLines,
230
240
  ...flowLines,
231
241
  ];
242
+ const commandToken = options.inputToken && options.inputToken === options.alias
243
+ ? options.inputToken
244
+ : projectionId;
232
245
  const nextActions = state === "running" && options.runningActions === false
233
246
  ? []
234
- : nextActionLines(options.alias ?? projectionId, result, state);
247
+ : nextActionLines(commandToken, result, state);
235
248
  if (nextActions.length > 0)
236
249
  lines.push("");
237
250
  lines.push(...nextActions);
238
- const response = coreTextResponse(lines.join("\n"), result);
251
+ const response = withPayload(coreTextResponse(lines.join("\n"), result));
252
+ if (result.state === "timeout")
253
+ return { ...response, exitCode: 124 };
239
254
  if (snapshot.phase === "dismissed")
240
255
  return { ...response, exitCode: 130 };
241
- if (["failed", "lost", "startup-timeout", "unknown"].includes(snapshot.phase)) {
242
- return { ...response, exitCode: 1 };
256
+ if (result.state === "lost") {
257
+ return { ...response, exitCode: 4 };
243
258
  }
244
259
  return response;
245
260
  }
@@ -247,11 +262,20 @@ export function buildWaitResponse(projectionId, result, options = {}) {
247
262
  export function buildProjectionWaitSetResponse(result) {
248
263
  const rendered = result.results
249
264
  .slice()
250
- .sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0)
265
+ .sort((left, right) => waitTriage(left) - waitTriage(right)
266
+ || latestWaitActivity(right) - latestWaitActivity(left)
267
+ || terminalWaitTime(right) - terminalWaitTime(left)
268
+ || left.id.localeCompare(right.id))
251
269
  .map((member) => ({
252
270
  id: member.id,
253
271
  result: member.result,
254
- response: buildWaitResponse(member.id, member.result, { runningActions: false }),
272
+ inputToken: member.inputToken,
273
+ alias: member.alias,
274
+ response: buildWaitResponse(member.id, member.result, {
275
+ alias: member.alias,
276
+ inputToken: member.inputToken,
277
+ runningActions: false,
278
+ }),
255
279
  }));
256
280
  const liveIds = rendered
257
281
  .filter((member) => member.result.state === "timeout")
@@ -260,16 +284,60 @@ export function buildProjectionWaitSetResponse(result) {
260
284
  if (liveIds.length > 0) {
261
285
  blocks.push([
262
286
  "Come back for the result:",
263
- ` » keiyaku wait ${liveIds.join(" ")} --${result.selection} [--timeout DURATION]`,
287
+ ` » keiyaku wait ${rendered.filter((member) => member.result.state === "timeout").map((member) => member.inputToken ?? member.id).join(" ")} --${result.selection} [--timeout DURATION]`,
264
288
  "Steer them while they work:",
265
289
  ` » ${formatTellCommand("<projection-id>", '"your words"')}`,
266
290
  ].join("\n"));
267
291
  }
268
292
  const text = blocks.join("\n\n");
293
+ const payload = {
294
+ selection: result.selection,
295
+ ...(result.selectedId ? { selectedId: result.selectedId } : {}),
296
+ results: rendered.map((member) => ({
297
+ canonicalId: member.id,
298
+ ...(member.inputToken ? { inputToken: member.inputToken } : {}),
299
+ ...(member.alias ? { alias: member.alias } : {}),
300
+ result: member.result,
301
+ })),
302
+ };
303
+ const response = coreTextResponse(text, payload);
304
+ const conditionSatisfied = result.selection === "any" && result.selectedId !== undefined;
269
305
  const exitCode = rendered.reduce((current, { response }) => {
306
+ if (current === 130)
307
+ return current;
270
308
  if (response.exitCode === 130)
271
309
  return 130;
272
- return response.exitCode === 1 ? 1 : current;
310
+ if (current === 4)
311
+ return current;
312
+ if (response.exitCode === 4)
313
+ return 4;
314
+ return response.exitCode === 124 && !conditionSatisfied ? 124 : current;
273
315
  }, undefined);
274
- return exitCode === undefined ? textResponse(text) : { ...textResponse(text), exitCode };
316
+ const deadline = !conditionSatisfied && rendered.some((member) => member.result.state === "timeout");
317
+ return exitCode === undefined && !deadline ? response : { ...response, exitCode: exitCode ?? 124 };
318
+ }
319
+ function waitSnapshot(result) {
320
+ return result.state === "unknown" ? undefined : result.snapshot;
321
+ }
322
+ function waitTriage(member) {
323
+ const snapshot = waitSnapshot(member.result);
324
+ if (member.result.state === "unknown" || member.result.state === "lost")
325
+ return 0;
326
+ if (member.result.state === "terminal" && (member.result.verdict.terminalFailure || member.result.verdict.state !== "completed"))
327
+ return 0;
328
+ if ((snapshot?.eventDiagnostics?.length ?? 0) > 0)
329
+ return 0;
330
+ return member.result.state === "timeout" ? 1 : 2;
331
+ }
332
+ function latestWaitActivity(member) {
333
+ const snapshot = waitSnapshot(member.result);
334
+ if (!snapshot || member.result.state !== "timeout")
335
+ return Number.NEGATIVE_INFINITY;
336
+ return snapshot.observedAtMs - (snapshot.activityAgeMs ?? snapshot.durationMs);
337
+ }
338
+ function terminalWaitTime(member) {
339
+ if (member.result.state !== "terminal")
340
+ return Number.NEGATIVE_INFINITY;
341
+ const parsed = Date.parse(member.result.verdict.at);
342
+ return Number.isFinite(parsed) ? parsed : Number.NEGATIVE_INFINITY;
275
343
  }
@@ -1 +1,2 @@
1
- export const TASK_SUBCOMMANDS = ["add", "show", "ls", "log", "start", "stop", "hold", "resume", "done", "drop", "update", "doctor"];
1
+ export const TASK_SUBCOMMANDS = ["add", "compose", "show", "ls", "log", "start", "stop", "hold", "resume", "done", "note", "drop", "update", "doctor"];
2
+ export const AKUMA_SUBCOMMANDS = ["list", "ls", "show"];
@@ -6,6 +6,7 @@ import { observeGitRepository } from "../git/branches.js";
6
6
  import { BUILTIN_AGENT_PROFILES } from "../keiyaku.js";
7
7
  import { getKeiyakuHome } from "./env.js";
8
8
  import { agentNameSchema, providerInstanceNameSchema } from "./settings/schema.js";
9
+ import { canonicalizeAkumaName } from "../core/projection/akuma-name.js";
9
10
  const nonblankString = z.string().refine((value) => value.trim().length > 0, { message: "must not be blank" });
10
11
  const effortOptionsSchema = z.array(nonblankString).superRefine((values, ctx) => {
11
12
  if (new Set(values).size !== values.length) {
@@ -129,9 +130,28 @@ async function readLayer(directory, source) {
129
130
  };
130
131
  }
131
132
  const candidates = [];
133
+ const canonicalFiles = new Map();
134
+ for (const entry of entries.filter((candidate) => candidate.isFile() && candidate.name.endsWith(".md"))) {
135
+ const rawName = path.basename(entry.name, ".md");
136
+ const canonicalName = canonicalizeAkumaName(rawName);
137
+ const files = canonicalFiles.get(canonicalName) ?? [];
138
+ files.push(entry.name);
139
+ canonicalFiles.set(canonicalName, files);
140
+ }
132
141
  for (const entry of entries.filter((candidate) => candidate.isFile() && candidate.name.endsWith(".md")).sort((a, b) => a.name.localeCompare(b.name))) {
133
142
  const file = path.join(directory, entry.name);
134
- const name = path.basename(entry.name, ".md");
143
+ const rawName = path.basename(entry.name, ".md");
144
+ const name = canonicalizeAkumaName(rawName);
145
+ const collisions = canonicalFiles.get(name);
146
+ if (collisions.length > 1) {
147
+ candidates.push(freezeCandidate({
148
+ name,
149
+ source,
150
+ path: file,
151
+ reason: `akuma name collision after Unicode NFC normalization: ${collisions.sort().join(", ")}`,
152
+ }));
153
+ continue;
154
+ }
135
155
  const nameResult = agentNameSchema.safeParse(name);
136
156
  if (!nameResult.success) {
137
157
  candidates.push(freezeCandidate({
@@ -1,5 +1,11 @@
1
- export const KNOWN_SETTINGS_KNOBS = ["default", "reviewer", "gate", "defaultBranch", "worktreeBootstrap"];
1
+ export const PROJECTION_ADOPTION_TIMEOUT_DEFAULT_MS = 30_000;
2
+ export const PROJECTION_ADOPTION_TIMEOUT_MIN_MS = 1_000;
3
+ export const PROJECTION_ADOPTION_TIMEOUT_MAX_MS = 600_000;
4
+ export const KNOWN_SETTINGS_KNOBS = ["default", "reviewer", "gate", "defaultBranch", "worktreeBootstrap", "projectionAdoptionTimeoutMs"];
2
5
  export const KNOWN_SETTINGS_ROOT_KEYS = [...KNOWN_SETTINGS_KNOBS, "providers"];
6
+ export function resolveProjectionAdoptionTimeoutMs(settings) {
7
+ return settings?.projectionAdoptionTimeoutMs ?? PROJECTION_ADOPTION_TIMEOUT_DEFAULT_MS;
8
+ }
3
9
  export function resolveDefaultAgentName(settings) {
4
10
  return settings?.default ?? "akuma-codex";
5
11
  }
@@ -1,5 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { BUILTIN_PROVIDER_INSTANCE_CANDIDATES as REGISTRY_BUILTIN_PROVIDER_INSTANCE_CANDIDATES, providerInstanceSchema as registryProviderInstanceSchema, } from "../../agents/providers/registry.js";
3
+ import { akumaNameSchema } from "../../core/projection/akuma-name.js";
4
+ import { PROJECTION_ADOPTION_TIMEOUT_MAX_MS, PROJECTION_ADOPTION_TIMEOUT_MIN_MS, } from "./knobs.js";
3
5
  export const providerInstanceSchema = registryProviderInstanceSchema;
4
6
  export const BUILTIN_PROVIDER_INSTANCE_CANDIDATES = REGISTRY_BUILTIN_PROVIDER_INSTANCE_CANDIDATES;
5
7
  const PROVIDER_INSTANCE_NAME_PATTERN = /^[a-z0-9][a-z0-9-]*$/;
@@ -10,11 +12,7 @@ export const providerInstanceNameSchema = z
10
12
  // Ensure the registry builtin map stays keyed by the closed provider vocabulary.
11
13
  const _builtinCheck = BUILTIN_PROVIDER_INSTANCE_CANDIDATES;
12
14
  void _builtinCheck;
13
- const AGENT_NAME_PATTERN = /^[a-zA-Z0-9]+(?:[.-][a-zA-Z0-9]+)*$/;
14
- export const agentNameSchema = z
15
- .string()
16
- .min(1, "must not be empty.")
17
- .regex(AGENT_NAME_PATTERN, "must contain only alphanumeric characters, dots, and hyphens, and must not start or end with a separator.");
15
+ export const agentNameSchema = akumaNameSchema;
18
16
  function optionalTrimmedString() {
19
17
  return z.string().transform((value) => {
20
18
  const trimmed = value.trim();
@@ -62,6 +60,11 @@ const gateSchema = z
62
60
  const WORKTREE_BOOTSTRAP_DEFAULT_TIMEOUT_MS = 300_000;
63
61
  const WORKTREE_BOOTSTRAP_MIN_TIMEOUT_MS = 1;
64
62
  const WORKTREE_BOOTSTRAP_MAX_TIMEOUT_MS = 3_600_000;
63
+ export const projectionAdoptionTimeoutSchema = z
64
+ .number()
65
+ .int("projectionAdoptionTimeoutMs must be an integer.")
66
+ .min(PROJECTION_ADOPTION_TIMEOUT_MIN_MS, `projectionAdoptionTimeoutMs must be >= ${PROJECTION_ADOPTION_TIMEOUT_MIN_MS}.`)
67
+ .max(PROJECTION_ADOPTION_TIMEOUT_MAX_MS, `projectionAdoptionTimeoutMs must be <= ${PROJECTION_ADOPTION_TIMEOUT_MAX_MS}.`);
65
68
  export const worktreeBootstrapSchema = z
66
69
  .object({
67
70
  command: z
@@ -97,6 +100,7 @@ const settingsSchema = z
97
100
  gate: gateSchema.optional(),
98
101
  defaultBranch: optionalTrimmedString(),
99
102
  worktreeBootstrap: worktreeBootstrapSchema.optional(),
103
+ projectionAdoptionTimeoutMs: projectionAdoptionTimeoutSchema.optional(),
100
104
  })
101
105
  .strict();
102
106
  export const knobSchemas = {
@@ -105,4 +109,5 @@ export const knobSchemas = {
105
109
  gate: gateSchema,
106
110
  defaultBranch: optionalTrimmedString(),
107
111
  worktreeBootstrap: worktreeBootstrapSchema,
112
+ projectionAdoptionTimeoutMs: projectionAdoptionTimeoutSchema,
108
113
  };
@@ -237,7 +237,7 @@ export function resolveExplicitContractAddressFromRead(input) {
237
237
  const source = input.source ?? (input.raw.startsWith("@") ? "at-address" : "explicit-flag");
238
238
  const spelling = { asTyped: commissionSelectorAsTyped(query, source), source };
239
239
  if (isResponseArtifactId(query)) {
240
- throw new AddressResolutionError("EMPTY_PARAM", `artifact ${query} is a response artifact; expected a commission address`, { repo, workdir, expected: "commission", spelling, candidates: [], inference: [], actual: "artifact" });
240
+ throw new AddressResolutionError("UNKNOWN_ADDRESS", `artifact ${query} is a response artifact; expected a commission address`, { repo, workdir, expected: "commission", spelling, candidates: [], inference: [], actual: "artifact" });
241
241
  }
242
242
  const identities = explicitAddressIdentities(input.read, query);
243
243
  const attempt = {
@@ -282,7 +282,7 @@ export function resolveExplicitContractAddressFromRead(input) {
282
282
  if (unknown.status === "diseased" || unknown.status === "isolated" || unknown.status === "indeterminate") {
283
283
  throw contractReadOutcomeError(query, unknown);
284
284
  }
285
- throw new AddressResolutionError("EMPTY_PARAM", `unknown contract address @${query}`, attempt);
285
+ throw new AddressResolutionError("UNKNOWN_ADDRESS", `unknown contract address @${query}`, attempt);
286
286
  }
287
287
  export async function resolveContractAddress(cwd, raw, source = raw?.startsWith("@") ? "at-address" : "explicit-flag", effectiveDirectory = cwd) {
288
288
  const workdir = path.resolve(effectiveDirectory);
@@ -348,7 +348,7 @@ export async function resolveContractAddress(cwd, raw, source = raw?.startsWith(
348
348
  inference: attempt.inference,
349
349
  });
350
350
  }
351
- throw new AddressResolutionError(operable.length === 0 ? "EMPTY_PARAM" : "CONTRACT_AMBIGUOUS", operable.length === 0
351
+ throw new AddressResolutionError(operable.length === 0 ? "UNKNOWN_ADDRESS" : "CONTRACT_AMBIGUOUS", operable.length === 0
352
352
  ? "no active commission found; pass @place, @slug, full @commission-id, or --contract <addr>"
353
353
  : "multiple active commissions found; pass @place, @slug, full @commission-id, or --contract <addr>", attempt);
354
354
  }
@@ -9,18 +9,31 @@ import { markdownSourceSections, normalizeScopePattern, ScopePatternError, valid
9
9
  import { normalizeSectionTitle } from "./markdown/titles.js";
10
10
  import { isCompleteLifecycleOutcome } from "./outcome-base.js";
11
11
  import { getConfig } from "../config/env.js";
12
+ import { parseVerificationDeclarations } from "./verification-declaration.js";
12
13
  function amendScopeHeading(name) {
13
14
  return `## Scope ${name[0].toUpperCase()}${name.slice(1)}`;
14
15
  }
16
+ function normalizeReplaceVerificationsTitle(title) {
17
+ // ATX closing markers are structural syntax, not part of the section title.
18
+ return normalizeSectionTitle(title.replace(/[ \t]+#+[ \t]*$/, ""));
19
+ }
15
20
  function parseAmendScopeSections(markdown) {
16
21
  const sections = {};
17
22
  for (const section of markdownSourceSections(markdown)) {
18
23
  if (section.level !== 2)
19
24
  continue;
20
25
  const title = normalizeSectionTitle(section.title);
26
+ if (normalizeReplaceVerificationsTitle(section.title) === "replace verifications")
27
+ continue;
21
28
  if (title === "scope add") {
22
29
  throw new FlowError("INVALID_KEIYAKU_DRAFT", "Scope Add is obsolete; use ## Scope Append with a fenced gitignore-subset block");
23
30
  }
31
+ if (title.startsWith("scope ") && title !== "scope append") {
32
+ throw new FlowError("INVALID_KEIYAKU_DRAFT", `unknown structured amendment operation ## ${section.title}`);
33
+ }
34
+ if (title.startsWith("replace ") && title !== "replace verifications") {
35
+ throw new FlowError("INVALID_KEIYAKU_DRAFT", `unknown structured amendment operation ## ${section.title}`);
36
+ }
24
37
  if (title !== "scope append")
25
38
  continue;
26
39
  if (sections.append) {
@@ -33,11 +46,7 @@ function parseAmendScopeSections(markdown) {
33
46
  end: section.end,
34
47
  };
35
48
  }
36
- let prose = markdown;
37
- for (const section of Object.values(sections).sort((left, right) => right.start - left.start)) {
38
- prose = `${prose.slice(0, section.start)}${prose.slice(section.end)}`;
39
- }
40
- return { sections, prose };
49
+ return { sections };
41
50
  }
42
51
  function scopePatterns(markdown, section) {
43
52
  return validateScopeFragmentLines(markdown.slice(section.contentStart, section.end).split(/\r?\n/), {
@@ -56,29 +65,90 @@ function normalizeAmendScopePatterns(patterns) {
56
65
  throw error;
57
66
  }
58
67
  }
68
+ /** Inspect every structural ATX level so a misplaced replacement never becomes prose. */
69
+ function amendAtxHeadings(markdown) {
70
+ const lines = markdown.split(/\r?\n/);
71
+ const frontMatterEnd = lines[0]?.trim() === "---"
72
+ ? lines.findIndex((line, index) => index > 0 && line.trim() === "---")
73
+ : -1;
74
+ let fence;
75
+ const headings = [];
76
+ for (let index = 0; index < lines.length; index += 1) {
77
+ const line = lines[index];
78
+ if (index <= frontMatterEnd)
79
+ continue;
80
+ if (fence) {
81
+ const close = line.match(/^ {0,3}(`+|~+)[ \t]*$/);
82
+ if (close && close[1][0] === fence.delimiter && close[1].length >= fence.length)
83
+ fence = undefined;
84
+ continue;
85
+ }
86
+ const open = line.match(/^ {0,3}(`{3,}|~{3,})(.*)$/);
87
+ if (open && !(open[1][0] === "`" && open[2].includes("`"))) {
88
+ fence = { delimiter: open[1][0], length: open[1].length };
89
+ continue;
90
+ }
91
+ const heading = line.match(/^(#{1,6})(?!#)[ \t]+(.+)$/);
92
+ if (heading)
93
+ headings.push({ level: heading[1].length, title: heading[2] });
94
+ }
95
+ return headings;
96
+ }
97
+ function parseReplaceVerificationsSection(markdown) {
98
+ const matchingHeadings = amendAtxHeadings(markdown)
99
+ .filter((heading) => normalizeReplaceVerificationsTitle(heading.title) === "replace verifications");
100
+ if (matchingHeadings.some((heading) => heading.level !== 2)) {
101
+ throw new FlowError("INVALID_KEIYAKU_DRAFT", "Replace Verifications must use the H2 heading '## Replace Verifications'");
102
+ }
103
+ if (matchingHeadings.length > 1) {
104
+ throw new FlowError("INVALID_KEIYAKU_DRAFT", "amend input may contain ## Replace Verifications at most once");
105
+ }
106
+ let replacement;
107
+ for (const section of markdownSourceSections(markdown)) {
108
+ if (normalizeReplaceVerificationsTitle(section.title) !== "replace verifications")
109
+ continue;
110
+ replacement = section;
111
+ }
112
+ return replacement;
113
+ }
114
+ function replacementDeclarations(markdown, section) {
115
+ return parseVerificationDeclarations(markdown.slice(section.contentStart, section.end), {
116
+ heading: "## Replace Verifications",
117
+ allowNone: true,
118
+ });
119
+ }
59
120
  export function buildAmendInput(markdown, cwd, contractId, contractAddressSource, appendScope) {
60
- const { sections, prose } = parseAmendScopeSections(markdown);
121
+ const { sections } = parseAmendScopeSections(markdown);
122
+ const replacement = parseReplaceVerificationsSection(markdown);
61
123
  if (sections.append && appendScope && appendScope.length > 0) {
62
124
  throw new FlowError("INVALID_KEIYAKU_DRAFT", "amend input may use either --append-scope or ## Scope Append, not both");
63
125
  }
64
126
  const append = sections.append
65
127
  ? scopePatterns(markdown, sections.append)
66
128
  : normalizeAmendScopePatterns(appendScope ?? []);
67
- if (!prose.trim()) {
129
+ const verification = replacement ? replacementDeclarations(markdown, replacement) : undefined;
130
+ const sourceSections = [...Object.values(sections), ...(replacement ? [replacement] : [])];
131
+ let proseWithoutReplacement = markdown;
132
+ for (const section of sourceSections.sort((left, right) => right.start - left.start)) {
133
+ proseWithoutReplacement = `${proseWithoutReplacement.slice(0, section.start)}${proseWithoutReplacement.slice(section.end)}`;
134
+ }
135
+ if (!proseWithoutReplacement.trim() && append.length === 0 && verification === undefined) {
68
136
  throw new FlowError("EMPTY_PARAM", "amendment cannot be empty");
69
137
  }
70
138
  return {
71
139
  cwd,
72
140
  ...(contractId ? { contractId } : {}),
73
141
  ...(contractAddressSource ? { contractAddressSource } : {}),
74
- amendment: prose,
142
+ amendment: proseWithoutReplacement,
75
143
  ...(append.length > 0 ? { scopeDelta: { add: append } } : {}),
144
+ ...(verification ? { verificationDelta: { replace: verification } } : {}),
76
145
  };
77
146
  }
78
- function requireAmendment(value) {
79
- if (!value.trim())
147
+ function requireAmendment(input) {
148
+ if (!input.amendment.trim() && !input.scopeDelta && !input.verificationDelta) {
80
149
  throw new FlowError("EMPTY_PARAM", "amendment cannot be empty");
81
- return value;
150
+ }
151
+ return input.amendment;
82
152
  }
83
153
  function assertContractOperable(contractId, model) {
84
154
  const state = currentLifecycleState(model);
@@ -95,9 +165,19 @@ function normalizeScopeDelta(scopeDelta) {
95
165
  return undefined;
96
166
  return { add };
97
167
  }
168
+ function normalizeVerificationDelta(verificationDelta) {
169
+ if (!verificationDelta)
170
+ return undefined;
171
+ const replace = verificationDelta.replace.map((declaration) => ({ ...declaration }));
172
+ if (replace.some((declaration) => !declaration.executor || !declaration.script.trim())) {
173
+ throw new FlowError("INVALID_KEIYAKU_DRAFT", "Verification replacement declarations must have an executor and nonblank script");
174
+ }
175
+ return { replace };
176
+ }
98
177
  export async function amendContract(input) {
99
- const amendment = requireAmendment(input.amendment);
100
178
  const scopeDelta = normalizeScopeDelta(input.scopeDelta);
179
+ const verificationDelta = normalizeVerificationDelta(input.verificationDelta);
180
+ const amendment = requireAmendment({ amendment: input.amendment, scopeDelta, verificationDelta });
101
181
  const at = new Date(input.nowMs).toISOString();
102
182
  const execution = await runLifecycle({
103
183
  cwd: input.cwd,
@@ -135,12 +215,16 @@ export async function amendContract(input) {
135
215
  return {
136
216
  amendmentNumber: admission.business.amendmentNumber,
137
217
  entry: {
138
- v: 1,
218
+ v: 2,
139
219
  kind: "amend",
140
220
  contract: input.contractId,
141
221
  at,
142
222
  actor: input.actor,
143
- data: { amendment, ...(scopeDelta ? { scopeDelta } : {}) },
223
+ data: {
224
+ amendment,
225
+ ...(scopeDelta ? { scopeDelta } : {}),
226
+ ...(verificationDelta ? { verificationDelta } : {}),
227
+ },
144
228
  },
145
229
  };
146
230
  },
@@ -162,12 +246,12 @@ export async function amendContract(input) {
162
246
  effect: async ({ context, admission, prepared }) => {
163
247
  const result = prepared.existing
164
248
  ? await context.replayCommitted({ transactionObjectId: prepared.existing.transactionObjectId })
165
- : await context.publishAuthoritative({ verb: "amend", admittedLedgerHead: admission.ledgerHead, acceptedLifecycleTail: context.session.lifecycle.history.at(-1).objectId, entries: [prepared.entry] });
249
+ : await context.publishAuthoritative({ verb: "amend", admittedLedgerHead: admission.ledgerHead, acceptedLifecycleTail: context.session.lifecycle.contractHead, entries: [prepared.entry] });
166
250
  if (result.status === "indeterminate") {
167
251
  return { kind: "precommit-effects-only", authoritativeHead: result.currentHead ?? admission.ledgerHead };
168
252
  }
169
253
  if (result.status !== "committed") {
170
- return { kind: "refused-before-effects", authoritativeHead: result.currentHead ?? context.session.lifecycle.observedLedgerHead, refusal: { code: "LEDGER_CAS_CONFLICT", message: `contract ${input.contractId} changed during amend` } };
254
+ return { kind: "refused-before-effects", authoritativeHead: result.currentHead ?? context.session.pins.repositoryLedgerHead, refusal: { code: "LEDGER_CAS_CONFLICT", message: `contract ${input.contractId} changed during amend` } };
171
255
  }
172
256
  return {
173
257
  kind: "committed",
@@ -204,7 +288,7 @@ export async function amendKeiyaku(input) {
204
288
  const address = input.address ?? await resolveContractAddress(input.cwd, input.contractId, input.contractAddressSource);
205
289
  return await withResolvedAddress(address, async () => {
206
290
  const contractId = address.binding.commissionId;
207
- const amendment = requireAmendment(input.amendment);
291
+ const amendment = requireAmendment(input);
208
292
  const amendmentWarningThreshold = getConfig().amendmentWarning.threshold;
209
293
  const amended = await amendContract({
210
294
  cwd: input.cwd,
@@ -214,6 +298,7 @@ export async function amendKeiyaku(input) {
214
298
  amendment,
215
299
  amendmentWarningThreshold,
216
300
  scopeDelta: input.scopeDelta,
301
+ verificationDelta: input.verificationDelta,
217
302
  signal: input.signal,
218
303
  });
219
304
  if (!isCompleteLifecycleOutcome(amended))
package/build/core/arc.js CHANGED
@@ -104,14 +104,14 @@ export async function openArc(input) {
104
104
  };
105
105
  }
106
106
  const scopeEntry = patterns.length === 0 ? null : {
107
- v: 1,
107
+ v: 2,
108
108
  kind: "amend",
109
109
  contract: input.contractId,
110
110
  at,
111
111
  actor: input.actor,
112
112
  data: { amendment: "Scope extended by lifecycle command.", scopeDelta: { add: patterns } },
113
113
  };
114
- const capture = await captureFoldedSeal(admission.business.targetRoot, admission.business.lifecycle, "HEAD", patterns);
114
+ const capture = await captureFoldedSeal(admission.business.targetRoot, admission.business.lifecycle, "HEAD", patterns, "refuse");
115
115
  const arcNumber = admission.business.lifecycle.intent.arc + 1;
116
116
  let sealEntry = null;
117
117
  if (capture) {
@@ -158,14 +158,14 @@ export async function openArc(input) {
158
158
  : await context.publishAuthoritative({
159
159
  verb: "arc",
160
160
  admittedLedgerHead: admission.ledgerHead,
161
- acceptedLifecycleTail: context.session.lifecycle.history.at(-1).objectId,
161
+ acceptedLifecycleTail: context.session.lifecycle.contractHead,
162
162
  entries: prepared.additions,
163
163
  });
164
164
  if (result.status === "indeterminate") {
165
165
  return { kind: "precommit-effects-only", authoritativeHead: result.currentHead ?? admission.ledgerHead };
166
166
  }
167
167
  if (result.status !== "committed" || prepared.arcNumber === 0) {
168
- return { kind: "refused-before-effects", authoritativeHead: result.status === "committed" ? result.head : result.currentHead ?? context.session.lifecycle.observedLedgerHead, refusal: { code: "LEDGER_CAS_CONFLICT", message: `contract ${input.contractId} changed during arc` } };
168
+ return { kind: "refused-before-effects", authoritativeHead: result.status === "committed" ? result.head : result.currentHead ?? context.session.pins.repositoryLedgerHead, refusal: { code: "LEDGER_CAS_CONFLICT", message: `contract ${input.contractId} changed during arc` } };
169
169
  }
170
170
  const committedCapture = prepared.capture;
171
171
  const sealed = committedCapture && {