@astrosheep/keiyaku 4.0.4 → 4.0.5

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 (173) hide show
  1. package/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +1 -1
  2. package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +1 -1
  3. package/build/integrations/marketplace/plugins/keiyaku/package.json +1 -1
  4. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +3 -3
  5. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +14 -0
  6. package/build/src/akuma/akuma.d.ts +0 -1
  7. package/build/src/akuma/akuma.js +1 -6
  8. package/build/src/akuma/allowed.d.ts +1 -1
  9. package/build/src/akuma/allowed.js +3 -10
  10. package/build/src/akuma/body.d.ts +5 -2
  11. package/build/src/akuma/body.js +29 -23
  12. package/build/src/akuma/heart/facts.d.ts +35 -9
  13. package/build/src/akuma/heart/index.d.ts +4 -13
  14. package/build/src/akuma/heart/index.js +2 -133
  15. package/build/src/akuma/heart/request-authority.d.ts +12 -0
  16. package/build/src/akuma/heart/request-authority.js +119 -0
  17. package/build/src/akuma/heart/request-rows.d.ts +16 -0
  18. package/build/src/akuma/heart/request-rows.js +88 -0
  19. package/build/src/akuma/heart/rows.d.ts +1 -28
  20. package/build/src/akuma/heart/rows.js +0 -137
  21. package/build/src/akuma/heart/schema.d.ts +1 -1
  22. package/build/src/akuma/heart/schema.js +9 -2
  23. package/build/src/akuma/heart/soul.js +0 -36
  24. package/build/src/akuma/index.d.ts +1 -1
  25. package/build/src/akuma/provider-recipe.js +1 -11
  26. package/build/src/akuma/provider.d.ts +1 -6
  27. package/build/src/akuma/providers/acp/core.js +6 -2
  28. package/build/src/akuma/providers/acp/index.js +0 -1
  29. package/build/src/akuma/providers/claude/index.d.ts +0 -4
  30. package/build/src/akuma/providers/claude/index.js +7 -3
  31. package/build/src/akuma/providers/codex-app-server/index.d.ts +2 -27
  32. package/build/src/akuma/providers/codex-app-server/index.js +0 -2
  33. package/build/src/akuma/providers/grok-build/index.js +0 -1
  34. package/build/src/akuma/providers/opencode-sdk/index.d.ts +2 -28
  35. package/build/src/akuma/providers/opencode-sdk/index.js +8 -4
  36. package/build/src/akuma/providers/opencode-sdk/session.d.ts +1 -1
  37. package/build/src/akuma/providers/opencode-sdk/session.js +1 -1
  38. package/build/src/akuma/providers/pi/index.js +11 -4
  39. package/build/src/akuma/request-serve.d.ts +87 -0
  40. package/build/src/akuma/request-serve.js +484 -0
  41. package/build/src/akuma/request-wire.d.ts +124 -0
  42. package/build/src/akuma/request-wire.js +380 -0
  43. package/build/src/akuma/requests.d.ts +28 -94
  44. package/build/src/akuma/requests.js +52 -562
  45. package/build/src/akuma-body.d.ts +7 -0
  46. package/build/src/akuma-body.js +54 -1
  47. package/build/src/body/arc.js +4 -29
  48. package/build/src/body/decode.js +5 -39
  49. package/build/src/body/envelope.d.ts +7 -0
  50. package/build/src/body/envelope.js +37 -0
  51. package/build/src/cli/accepted.js +17 -9
  52. package/build/src/cli/commands/akuma.js +2 -2
  53. package/build/src/cli/commands/contract.d.ts +6 -6
  54. package/build/src/cli/commands/contract.js +3 -3
  55. package/build/src/cli/commands/task-invoke.js +98 -9
  56. package/build/src/cli/coordinates.d.ts +1 -0
  57. package/build/src/cli/coordinates.js +7 -4
  58. package/build/src/cli/invoke.js +28 -4
  59. package/build/src/cli/parse.d.ts +2 -2
  60. package/build/src/cli/parse.js +21 -8
  61. package/build/src/cli/render/akuma.js +56 -13
  62. package/build/src/cli/render/audit.js +2 -6
  63. package/build/src/cli/render/contract.js +40 -0
  64. package/build/src/cli/render/receipt.js +28 -1
  65. package/build/src/cli/render/terminal.d.ts +0 -2
  66. package/build/src/cli/render/terminal.js +0 -29
  67. package/build/src/cli/result.d.ts +15 -7
  68. package/build/src/contract-worktree.d.ts +1 -2
  69. package/build/src/contract-worktree.js +4 -9
  70. package/build/src/core/facts/codec.js +9 -0
  71. package/build/src/core/facts/fold.js +13 -1
  72. package/build/src/core/facts/types.d.ts +8 -1
  73. package/build/src/core/subject.js +4 -3
  74. package/build/src/core/verbs/placement.d.ts +9 -1
  75. package/build/src/core/verbs/placement.js +24 -6
  76. package/build/src/core/verbs/reintegrate.d.ts +13 -0
  77. package/build/src/core/verbs/reintegrate.js +24 -0
  78. package/build/src/dispatch/index.d.ts +1 -1
  79. package/build/src/git/admission.d.ts +2 -1
  80. package/build/src/git/integration.d.ts +5 -2
  81. package/build/src/git/integration.js +54 -1
  82. package/build/src/git/observe.d.ts +2 -1
  83. package/build/src/git/observe.js +6 -5
  84. package/build/src/git/process.d.ts +26 -0
  85. package/build/src/git/process.js +94 -0
  86. package/build/src/git/read-observation.d.ts +2 -1
  87. package/build/src/git/read-observation.js +3 -2
  88. package/build/src/git/reconcile.d.ts +2 -1
  89. package/build/src/git/reconcile.js +9 -6
  90. package/build/src/git/repository.d.ts +2 -29
  91. package/build/src/git/repository.js +4 -118
  92. package/build/src/git/scratch.d.ts +1 -1
  93. package/build/src/git/scratch.js +1 -1
  94. package/build/src/git/target-placement.d.ts +1 -1
  95. package/build/src/git/target-placement.js +9 -7
  96. package/build/src/git/tender.d.ts +1 -1
  97. package/build/src/git/tender.js +2 -1
  98. package/build/src/git/terminal-seal.d.ts +2 -2
  99. package/build/src/git/terminal-seal.js +3 -6
  100. package/build/src/git/workspace.d.ts +1 -1
  101. package/build/src/git/workspace.js +1 -1
  102. package/build/src/index.d.ts +1 -2
  103. package/build/src/index.js +1 -1
  104. package/build/src/library/address.js +7 -1
  105. package/build/src/library/audit.d.ts +2 -1
  106. package/build/src/library/audit.js +1 -1
  107. package/build/src/library/bind.d.ts +2 -1
  108. package/build/src/library/bind.js +1 -1
  109. package/build/src/library/configuration.d.ts +1 -1
  110. package/build/src/library/configuration.js +52 -18
  111. package/build/src/library/contract.d.ts +55 -2
  112. package/build/src/library/contract.js +162 -23
  113. package/build/src/library/delivery.d.ts +1 -1
  114. package/build/src/library/fleet.d.ts +27 -7
  115. package/build/src/library/fleet.js +79 -39
  116. package/build/src/library/input.d.ts +0 -1
  117. package/build/src/library/input.js +0 -5
  118. package/build/src/library/keiyaku.d.ts +1 -1
  119. package/build/src/library/mutation.d.ts +2 -1
  120. package/build/src/library/reconcile.d.ts +2 -1
  121. package/build/src/library/reconcile.js +3 -1
  122. package/build/src/library/region.d.ts +1 -1
  123. package/build/src/library/repo.d.ts +3 -1
  124. package/build/src/library/repo.js +8 -4
  125. package/build/src/protocol/abandon.d.ts +9 -0
  126. package/build/src/protocol/abandon.js +15 -0
  127. package/build/src/protocol/amend.d.ts +16 -0
  128. package/build/src/protocol/amend.js +77 -0
  129. package/build/src/protocol/arc.d.ts +6 -0
  130. package/build/src/protocol/arc.js +12 -0
  131. package/build/src/protocol/attempt.d.ts +2 -1
  132. package/build/src/protocol/audit.d.ts +53 -0
  133. package/build/src/protocol/audit.js +140 -0
  134. package/build/src/protocol/bind.d.ts +1 -1
  135. package/build/src/protocol/completion.d.ts +32 -0
  136. package/build/src/protocol/completion.js +130 -0
  137. package/build/src/protocol/deliver.d.ts +37 -0
  138. package/build/src/protocol/deliver.js +160 -0
  139. package/build/src/protocol/intent.d.ts +1 -1
  140. package/build/src/protocol/intent.js +1 -1
  141. package/build/src/protocol/operations.d.ts +64 -200
  142. package/build/src/protocol/operations.js +21 -640
  143. package/build/src/protocol/placement.d.ts +4 -13
  144. package/build/src/protocol/placement.js +3 -15
  145. package/build/src/protocol/read/status.d.ts +1 -1
  146. package/build/src/protocol/read/status.js +3 -1
  147. package/build/src/protocol/reconcile.d.ts +35 -0
  148. package/build/src/protocol/reconcile.js +41 -0
  149. package/build/src/protocol/reintegrate.d.ts +31 -0
  150. package/build/src/protocol/reintegrate.js +74 -0
  151. package/build/src/protocol/review.d.ts +33 -0
  152. package/build/src/protocol/review.js +148 -0
  153. package/build/src/protocol/run.d.ts +1 -1
  154. package/build/src/settlement/fence.d.ts +1 -1
  155. package/build/src/settlement/holder.d.ts +1 -1
  156. package/build/src/settlement/settle.d.ts +1 -1
  157. package/build/src/task/board.d.ts +0 -1
  158. package/build/src/task/board.js +0 -3
  159. package/build/src/task/compose.d.ts +1 -1
  160. package/build/src/task/compose.js +11 -4
  161. package/build/src/task/index.d.ts +1 -1
  162. package/build/src/task/index.js +2 -127
  163. package/build/src/task/input.d.ts +18 -0
  164. package/build/src/task/input.js +189 -0
  165. package/build/src/task/mutation.d.ts +39 -0
  166. package/build/src/task/mutation.js +85 -0
  167. package/build/src/task/operations.d.ts +2 -0
  168. package/build/src/task/operations.js +35 -11
  169. package/build/src/task/store.js +0 -2
  170. package/build/src/verification/execution.d.ts +1 -1
  171. package/build/src/workspace-place.d.ts +1 -4
  172. package/build/src/workspace-place.js +0 -15
  173. package/package.json +3 -3
@@ -1,7 +1,7 @@
1
1
  import { isTaskAction, parseTaskCommand, renderTaskUsage, } from "./commands/task.js";
2
2
  import { isAkumaAction, parseAkumaCatalogPath, parseAkumaCommand, renderAkumaRootRows, renderAkumaUsage, } from "./commands/akuma.js";
3
3
  import { INSTALL_USAGE, parseInstallCommand, renderInstallHelp } from "./commands/install.js";
4
- import { AMEND_MINIMAL_STDIN_HELP, CONTRACT_COMMAND_SPECS } from "./commands/contract.js";
4
+ import { AMEND_MINIMAL_STDIN_HELP, CONTRACT_COMMAND_SPECS, } from "./commands/contract.js";
5
5
  import { CliUsageError, isBlankInput, usageLine, withJsonAutomationHelp } from "./usage.js";
6
6
  export { CliUsageError } from "./usage.js";
7
7
  const ROOT_USAGE = "usage: keiyaku [-C <path>] [--repo <path>] <command> [<contract>|@<contract>] [--flag ...] [-]";
@@ -69,9 +69,12 @@ function scanOption(command, argv, state, index) {
69
69
  return index;
70
70
  }
71
71
  const value = argv[index + 1];
72
- if (value === undefined || value === "-" || value.startsWith("--"))
72
+ if (value === undefined)
73
73
  refuse(command, `${token} requires a value`);
74
- if (isBlankInput(value))
74
+ if (kind !== "raw-value" && (value === "-" || value.startsWith("--"))) {
75
+ refuse(command, `${token} requires a value`);
76
+ }
77
+ if (kind !== "raw-value" && isBlankInput(value))
75
78
  refuse(command, `${token} requires a nonblank value`);
76
79
  if (kind === "repeat-value") {
77
80
  const values = state.flags[name];
@@ -121,7 +124,7 @@ function parseBind(parts) {
121
124
  const task = optionalFlag(parts.flags, "task");
122
125
  const target = optionalFlag(parts.flags, "target");
123
126
  const after = parts.flags.after === undefined ? [] : Array.isArray(parts.flags.after) ? parts.flags.after : [parts.flags.after];
124
- const gates = optionalFlag(parts.flags, "gates");
127
+ const gates = parseGateBundleNames(parts, "bind");
125
128
  return {
126
129
  command: "bind",
127
130
  ...(task === undefined ? {} : { task }),
@@ -138,7 +141,7 @@ function parseAmend(parts) {
138
141
  const after = parts.flags.after === undefined ? [] : Array.isArray(parts.flags.after) ? parts.flags.after : [parts.flags.after];
139
142
  if (parts.flags["clear-after"] === true && after.length > 0)
140
143
  refuse("amend", "--clear-after and --after are mutually exclusive");
141
- const gates = optionalFlag(parts.flags, "gates");
144
+ const gates = parseGateBundleNames(parts, "amend");
142
145
  if (!parts.stdin && parts.flags.after === undefined && parts.flags["clear-after"] !== true && gates === undefined) {
143
146
  refuse("amend", "amend requires stdin or --after, --clear-after, or --gates");
144
147
  }
@@ -153,6 +156,16 @@ function parseAmend(parts) {
153
156
  output: parts.output,
154
157
  };
155
158
  }
159
+ function parseGateBundleNames(parts, command) {
160
+ const value = optionalFlag(parts.flags, "gates");
161
+ if (value === undefined)
162
+ return undefined;
163
+ const names = value.split(",");
164
+ if (names.some((name) => name.length === 0)) {
165
+ refuse(command, "--gates requires nonempty comma-separated names");
166
+ }
167
+ return names;
168
+ }
156
169
  function parseDeliver(parts) {
157
170
  const contract = parts.positionals[0];
158
171
  const message = optionalFlag(parts.flags, "message");
@@ -240,14 +253,14 @@ function parseRegion(parts) {
240
253
  }
241
254
  function parseLs(parts) {
242
255
  const path = parts.positionals[0];
243
- if (path === "task/")
256
+ if (path === "task" || path === "task/")
244
257
  return { command: "ls", query: { kind: "tasks" }, output: parts.output };
245
- if (path === "kei/")
258
+ if (path === "kei" || path === "kei/")
246
259
  return { command: "ls", query: { kind: "contracts" }, output: parts.output };
247
260
  try {
248
261
  const query = parseAkumaCatalogPath(path);
249
262
  if (query === null)
250
- refuse("ls", "ls requires an identity directory with a trailing slash");
263
+ refuse("ls", "ls requires a supported identity directory selector");
251
264
  return { command: "ls", query, output: parts.output };
252
265
  }
253
266
  catch (error) {
@@ -7,16 +7,27 @@ const VERB_WIDTH = 6;
7
7
  function identity(id, alias) {
8
8
  return `${id}${alias === undefined ? "" : ` (${alias})`}`;
9
9
  }
10
- function associatedIdentity(id, alias, contractId) {
10
+ function associatedContractId(contract) {
11
+ return contract.kind === "associated" ? contract.contractId : undefined;
12
+ }
13
+ function associatedIdentity(id, alias, contract) {
14
+ const contractId = contract === undefined ? undefined : associatedContractId(contract);
11
15
  return `${identity(id, alias)}${contractId === undefined ? "" : ` [${contractId}]`}`;
12
16
  }
13
- function snapshotHeading(id, alias, contractId) {
17
+ function snapshotHeading(id, alias, contract) {
18
+ const contractId = contract === undefined ? undefined : associatedContractId(contract);
14
19
  return [
15
20
  OPENING_STROKE,
16
21
  identity(id, alias),
17
22
  ...(contractId === undefined ? [] : [`└─ ${contractId}`]),
18
23
  ];
19
24
  }
25
+ function contractFacts(contract) {
26
+ return contract.kind === "failed" ? [`! contract failed ${safeText(contract.diagnostic)}`] : [];
27
+ }
28
+ function unobservedText(id, diagnostic) {
29
+ return `! ${id} unobserved: ${safeText(diagnostic)}`;
30
+ }
20
31
  function lifeLabel(life) {
21
32
  if (life === "running")
22
33
  return "● running";
@@ -201,11 +212,12 @@ function snapshotText(view, context, options = {}) {
201
212
  : groupedEntries(snapshot.entries, context);
202
213
  const facts = [
203
214
  ...(view.status.readonly?.enforcement === "none" ? [`! ${safeText(view.status.readonly.diagnostic)}`] : []),
215
+ ...contractFacts(view.contract),
204
216
  ...(options.facts ?? []),
205
217
  ];
206
218
  const footer = options.showLife === false ? [] : [` ${lifeLabel(view.status.life)}`];
207
219
  return [
208
- ...snapshotHeading(view.status.id, options.alias, view.contractId),
220
+ ...snapshotHeading(view.status.id, options.alias, view.contract),
209
221
  ...facts,
210
222
  ...activity,
211
223
  ...footer,
@@ -213,17 +225,33 @@ function snapshotText(view, context, options = {}) {
213
225
  ...renderTaskContextLines(view.createdTasks, context.columns),
214
226
  ].join("\n");
215
227
  }
228
+ function observationStageText(id, observation, context, options = {}) {
229
+ if (observation.kind === "unobserved")
230
+ return unobservedText(id, observation.diagnostic);
231
+ return snapshotText(observation, context, options);
232
+ }
233
+ function waitText(result, context) {
234
+ const alias = result.alias;
235
+ const blocks = [
236
+ ...result.result.observations.map((observation) => snapshotText(observation, context, { ...(alias === undefined ? {} : { alias }) })),
237
+ ...result.result.unobserved.map((member) => unobservedText(member.id, member.diagnostic)),
238
+ ];
239
+ return blocks.join("\n\n");
240
+ }
216
241
  function historyText(command, result, context) {
217
242
  if (command.last)
218
243
  return result.mode === "last" ? result.answer : "no answer retained";
219
244
  if (result.mode !== "page")
220
245
  throw new Error("history result lacks page");
221
- const contractId = result.historyResult.contractId;
222
- return [...snapshotHeading(result.akuma, result.alias, contractId), ...groupedRows(result.history.rows, context, true)].join("\n");
246
+ return [...snapshotHeading(result.akuma, result.alias, result.historyResult.contract), ...groupedRows(result.history.rows, context, true)].join("\n");
223
247
  }
224
248
  function tellText(result, context) {
225
249
  const facts = typeof result.result.tell.wake === "string" ? [] : [`! error ${safeText(result.result.tell.wake.diagnostic)}`];
226
- return snapshotText(result.result.observation, context, { ...(result.alias === undefined ? {} : { alias: result.alias }), facts, showLife: false });
250
+ return observationStageText(result.result.akuma, result.result.observation, context, {
251
+ ...(result.alias === undefined ? {} : { alias: result.alias }),
252
+ facts,
253
+ showLife: false,
254
+ });
227
255
  }
228
256
  function dispatchLines(stage) {
229
257
  if (stage.kind === "none" || stage.kind === "dispatched")
@@ -272,7 +300,9 @@ function callText(result, context) {
272
300
  facts.push(`alias failed ${result.result.alias.failure.kind} ${safeText(result.result.alias.failure.diagnostic)}`);
273
301
  const cwd = executionCwdLine(result.result);
274
302
  if (result.result.observation.kind === "detached") {
275
- const lines = [...snapshotHeading(result.result.akuma, alias, contractId), ...cwd, ...restraint, ...facts];
303
+ const lines = [...snapshotHeading(result.result.akuma, alias, contractId === undefined
304
+ ? { kind: "none" }
305
+ : { kind: "associated", contractId }), ...cwd, ...restraint, ...facts];
276
306
  if (!callFailed(result.result)) {
277
307
  const selector = result.result.alias.kind === "aliased" ? result.result.alias.alias.alias : result.result.akuma;
278
308
  lines.push(`$ keiyaku -C ${result.world} wait ${selector} --timeout 5m`);
@@ -280,9 +310,14 @@ function callText(result, context) {
280
310
  return lines.join("\n");
281
311
  }
282
312
  if (result.result.observation.kind === "failed") {
283
- return [...snapshotHeading(result.result.akuma, alias, contractId), ...cwd, ...restraint, ...facts, `! error ${safeText(result.result.observation.failure.diagnostic)}`].join("\n");
313
+ return [...snapshotHeading(result.result.akuma, alias, contractId === undefined
314
+ ? { kind: "none" }
315
+ : { kind: "associated", contractId }), ...cwd, ...restraint, ...facts, `! error ${safeText(result.result.observation.failure.diagnostic)}`].join("\n");
284
316
  }
285
- return snapshotText({ status: result.result.observation.status, ...(contractId === undefined ? {} : { contractId }) }, context, {
317
+ return snapshotText({
318
+ status: result.result.observation.status,
319
+ contract: contractId === undefined ? { kind: "none" } : { kind: "associated", contractId },
320
+ }, context, {
286
321
  ...(alias === undefined ? {} : { alias }),
287
322
  facts: [...cwd, ...facts],
288
323
  });
@@ -299,17 +334,25 @@ export function renderAkumaText(command, result, context = DEFAULT_CONTEXT) {
299
334
  case "call": return callText(result, context);
300
335
  case "status": return snapshotText(result.status, context, { ...(result.alias === undefined ? {} : { alias: result.alias }) });
301
336
  case "wait":
302
- return result.result.observations.map((observation) => snapshotText(observation, context, { ...(result.alias === undefined ? {} : { alias: result.alias }) })).join("\n\n");
303
- case "tell": return result.mode === "ordinary" ? tellText(result, context) : snapshotText(result.result.observation, context, { ...(result.alias === undefined ? {} : { alias: result.alias }), showLife: false });
337
+ return waitText(result, context);
338
+ case "tell":
339
+ return result.mode === "ordinary"
340
+ ? tellText(result, context)
341
+ : observationStageText(result.result.id, result.result.observation, context, {
342
+ ...(result.alias === undefined ? {} : { alias: result.alias }),
343
+ showLife: false,
344
+ });
304
345
  case "history":
305
346
  return historyText(command, result, context);
306
347
  case "fork": {
307
348
  if (result.receipt.kind !== "forked")
308
349
  return result.receipt.kind === "unknown-history" ? `${result.receipt.at} has no matching retained answered turn` : result.receipt.kind === "provider-cannot-fork" ? `${result.receipt.provider} cannot fork` : result.receipt.diagnostic;
309
350
  const contractId = result.receipt.dispatch.kind === "dispatched" ? result.receipt.dispatch.dispatch.contractId : undefined;
310
- return associatedIdentity(result.receipt.child, undefined, contractId);
351
+ return associatedIdentity(result.receipt.child, undefined, contractId === undefined
352
+ ? { kind: "none" }
353
+ : { kind: "associated", contractId });
311
354
  }
312
- case "kill": return result.result.results.map((member) => snapshotText(member.observation, context, { facts: [`kill ${member.evidence}`], ...(result.alias === undefined ? {} : { alias: result.alias }) })).join("\n\n");
355
+ case "kill": return result.result.results.map((member) => observationStageText(member.id, member.observation, context, { facts: [`kill ${member.evidence}`], ...(result.alias === undefined ? {} : { alias: result.alias }) })).join("\n\n");
313
356
  }
314
357
  }
315
358
  function callExitCode(result) {
@@ -1,6 +1,6 @@
1
1
  import { cleanupLines, leakLines, receiptPayload, receiptRow, stopLines, titleLines, } from "./receipt.js";
2
2
  import { renderRefusalFacts } from "./refusal.js";
3
- import { gitShortStat, renderBoundedTextBlock, renderOpaqueBlock, } from "./terminal.js";
3
+ import { gitShortStat, renderOpaqueBlock, } from "./terminal.js";
4
4
  const CHILD = " ";
5
5
  function workspaceEvidence(workspace, columns) {
6
6
  return renderOpaqueBlock(`workspace ${workspace.kind} ${workspace.path}`, CHILD, columns);
@@ -46,11 +46,7 @@ function verificationLines(verification, columns, addressed) {
46
46
  { text: `${verification.passed} of ${verification.total}` },
47
47
  ], columns);
48
48
  if (verification.summary !== undefined) {
49
- lines.push(...renderBoundedTextBlock(verification.summary, {
50
- first: `${CHILD}summary `,
51
- continuation: CHILD,
52
- columns,
53
- }));
49
+ receiptPayload(lines, "summary", verification.summary);
54
50
  }
55
51
  return lines;
56
52
  }
@@ -201,10 +201,37 @@ function renderAcceptedAmend(result, columns) {
201
201
  lines.push(...acceptedDeviations(result, columns), ...recordBlock(result, columns));
202
202
  return lines.join("\n");
203
203
  }
204
+ function reintegrationLines(result, complete, columns) {
205
+ const reintegrated = result.facts.filter((fact) => fact.kind === "reintegrated");
206
+ if (reintegrated.length === 0)
207
+ return [];
208
+ const lines = [];
209
+ for (const fact of reintegrated) {
210
+ receiptRow(lines, " ", "re-integrated", [
211
+ { text: fact.data.predecessor, opaque: true },
212
+ { text: "->" },
213
+ { text: fact.data.snapshot, opaque: true },
214
+ ], columns);
215
+ }
216
+ if (result.verificationVerdict !== undefined) {
217
+ receiptRow(lines, " ", "verification re-run", [
218
+ { text: "·" },
219
+ { text: result.verificationVerdict },
220
+ ], columns);
221
+ }
222
+ if (complete) {
223
+ receiptRow(lines, " ", "target", [
224
+ { text: "->" },
225
+ { text: reintegrated.at(-1).data.snapshot, opaque: true },
226
+ ], columns);
227
+ }
228
+ return lines;
229
+ }
204
230
  function renderAcceptedDeliver(result, columns) {
205
231
  const complete = result.facts.some((fact) => fact.kind === "claimed");
206
232
  const title = complete ? "delivered" : "deliver — not complete";
207
233
  const lines = titleLines("✓", title, result.contract, columns);
234
+ lines.push(...reintegrationLines(result, complete, columns));
208
235
  if (!complete)
209
236
  receiptRow(lines, " ", "candidate", [{ text: "kept" }], columns);
210
237
  if (result.verification !== undefined) {
@@ -229,12 +256,20 @@ function renderAcceptedDeliver(result, columns) {
229
256
  function renderAcceptedReview(result, columns) {
230
257
  const complete = result.facts.some((fact) => fact.kind === "claimed");
231
258
  const lines = titleLines("✓", `review ${result.verdict}`, result.contract, columns);
259
+ lines.push(...reintegrationLines(result, complete, columns));
232
260
  receiptRow(lines, complete ? "✓" : "!", "contract", [
233
261
  { text: complete ? "complete" : "not complete" },
234
262
  ], columns);
235
263
  if (result.placement !== undefined) {
236
264
  lines.push(...stopLines("completion", result.placement, columns, result.contract));
237
265
  }
266
+ if (result.verification !== undefined) {
267
+ lines.push(...stopLines("verification", result.verification, columns, result.contract));
268
+ }
269
+ if (result.cleanup !== undefined)
270
+ pushBlock(lines, cleanupLines(result.cleanup, columns));
271
+ if (result.leak !== undefined)
272
+ pushBlock(lines, leakLines(result.leak, columns));
238
273
  lines.push(...acceptedDeviations(result, columns), ...recordBlock(result, columns));
239
274
  return lines.join("\n");
240
275
  }
@@ -338,6 +373,11 @@ function journalBody(fact) {
338
373
  ` require-branches-to-be-up-to-date ${String(policy.requireBranchesToBeUpToDate)}`,
339
374
  ];
340
375
  }
376
+ case "reintegrated":
377
+ return [
378
+ ` predecessor ${fact.data.predecessor}`,
379
+ ` snapshot ${fact.data.snapshot}`,
380
+ ];
341
381
  case "attestation": {
342
382
  const lines = [
343
383
  ` gate ${fact.data.gate}`,
@@ -59,6 +59,32 @@ export function reuseLines(reuse, columns) {
59
59
  return [];
60
60
  return renderOpaqueBlock(`reuse verification ${reuse.entry} ${reuse.verdict}`, " ", columns);
61
61
  }
62
+ function prerequisiteRows(stop, columns) {
63
+ if (!("refusal" in stop) || stop.refusal?.kind !== "prerequisites-unsatisfied")
64
+ return [];
65
+ const lines = [];
66
+ for (const prerequisite of stop.refusal.unmet) {
67
+ receiptRow(lines, " ", "prerequisite", [
68
+ { text: prerequisite.contractId, opaque: true },
69
+ { text: "·" },
70
+ { text: prerequisite.state },
71
+ ], columns);
72
+ }
73
+ return lines;
74
+ }
75
+ function targetMovedDetail(stop) {
76
+ if ("integratedAt" in stop) {
77
+ return [
78
+ { text: stop.target, opaque: true },
79
+ { text: `${stop.integratedAt} -> ${stop.observed}`, opaque: true },
80
+ { text: `attempts=${stop.attempts}` },
81
+ ];
82
+ }
83
+ return [
84
+ { text: stop.target, opaque: true },
85
+ { text: `${stop.expected} -> ${stop.observed}`, opaque: true },
86
+ ];
87
+ }
62
88
  export function stopLines(label, stop, columns, addressed) {
63
89
  const detail = [];
64
90
  if ("refusal" in stop && stop.refusal !== undefined) {
@@ -76,12 +102,13 @@ export function stopLines(label, stop, columns, addressed) {
76
102
  detail.push({ text: `command=${stop.command}` }, { text: hookFailureSummary(stop.detail), opaque: true });
77
103
  }
78
104
  else if (stop.failure === "target-moved") {
79
- detail.push({ text: stop.target, opaque: true }, { text: `${stop.expected} -> ${stop.observed}`, opaque: true });
105
+ detail.push(...targetMovedDetail(stop));
80
106
  }
81
107
  }
82
108
  const lines = [];
83
109
  const state = label === "verification" ? "failed" : "blocked";
84
110
  receiptRow(lines, "!", label, [{ text: state }, { text: "·" }, ...detail], columns);
111
+ lines.push(...prerequisiteRows(stop, columns));
85
112
  if ("failure" in stop && stop.failure === "environment-failure" && "command" in stop) {
86
113
  appendHookPayload(lines, stop.detail);
87
114
  }
@@ -23,8 +23,6 @@ export declare function renderBoundedTextBlock(value: string, input: Readonly<{
23
23
  truncated?: boolean;
24
24
  }>): readonly string[];
25
25
  export declare function safeText(value: string): string;
26
- export declare function renderFacts(primary: string, facts: readonly string[], columns: number): readonly string[];
27
26
  export declare function renderTextBlock(value: string, indent: string, columns: number): readonly string[];
28
27
  export declare function renderOpaqueBlock(value: string, indent: string, columns: number): readonly string[];
29
- export declare function renderVoiceRuler(left: string, right: string, columns: number): string;
30
28
  export declare function tone(value: string, kind: "dim" | "alert", color: boolean): string;
@@ -110,28 +110,6 @@ export function renderBoundedTextBlock(value, input) {
110
110
  export function safeText(value) {
111
111
  return value.replaceAll(/[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/gu, (character) => /\s/u.test(character) ? " " : "�");
112
112
  }
113
- export function renderFacts(primary, facts, columns) {
114
- const head = safeText(primary);
115
- const clean = facts.map(safeText);
116
- if (clean.length === 0)
117
- return [head];
118
- const inline = `${head} ${clean.join(" · ")}`;
119
- if (displayColumns(inline) <= columns)
120
- return [inline];
121
- const lines = [head];
122
- let current = " ";
123
- for (const fact of clean) {
124
- const candidate = current === " " ? `${current}${fact}` : `${current} · ${fact}`;
125
- if (current !== " " && displayColumns(candidate) > columns) {
126
- lines.push(current);
127
- current = ` ${fact}`;
128
- }
129
- else
130
- current = candidate;
131
- }
132
- lines.push(current);
133
- return lines;
134
- }
135
113
  export function renderTextBlock(value, indent, columns) {
136
114
  const words = safeText(value).trim().split(/\s+/u).filter((word) => word.length > 0);
137
115
  if (words.length === 0)
@@ -183,13 +161,6 @@ export function renderOpaqueBlock(value, indent, columns) {
183
161
  }
184
162
  return lines;
185
163
  }
186
- export function renderVoiceRuler(left, right, columns) {
187
- const width = Math.max(20, Math.min(80, columns));
188
- const occupied = displayColumns(left) + displayColumns(right) + 2;
189
- if (occupied >= width)
190
- return `${left}\n${"─".repeat(Math.max(1, width - displayColumns(right) - 1))} ${right}`;
191
- return `${left} ${"─".repeat(width - occupied)} ${right}`;
192
- }
193
164
  export function tone(value, kind, color) {
194
165
  if (!color)
195
166
  return value;
@@ -11,11 +11,19 @@ export type BindDraftReceipt = Readonly<{
11
11
  warning: string;
12
12
  }>;
13
13
  export type Effect = ReconcileReport["effects"][number];
14
- export type AcceptedFact = Readonly<{
14
+ type AcceptedFactEnvelope = Readonly<{
15
15
  contract: ContractId;
16
16
  entry: string;
17
- kind: Fact["kind"];
18
17
  }>;
18
+ export type AcceptedFact = (AcceptedFactEnvelope & Readonly<{
19
+ kind: Exclude<Fact["kind"], "reintegrated">;
20
+ data?: never;
21
+ }>) | (AcceptedFactEnvelope & Readonly<{
22
+ kind: "reintegrated";
23
+ data: Extract<Fact, {
24
+ kind: "reintegrated";
25
+ }>["data"];
26
+ }>);
19
27
  export type Lag = ReconcileReport["lag"][number];
20
28
  type MutationEnvelope = Pick<MutationResult<unknown>, "head" | "effects" | "settlement">;
21
29
  export type AcceptedEnvelope = Readonly<{
@@ -80,18 +88,18 @@ export type AcceptedDeliverResult = AcceptedEnvelope & Readonly<{
80
88
  export type AcceptedReviewResult = AcceptedEnvelope & Readonly<{
81
89
  verb: "review";
82
90
  verdict: "satisfied" | "unsatisfied";
91
+ verificationVerdict?: "satisfied" | "unsatisfied";
92
+ verification?: VerificationStop;
93
+ verificationReuse?: VerificationReuse;
83
94
  placement?: PlacementStop;
84
95
  workspace?: Review["workspace"];
96
+ cleanup?: MutationResult<unknown>["cleanup"];
97
+ leak?: MutationResult<unknown>["leak"];
85
98
  target?: never;
86
- verification?: never;
87
- verificationReuse?: never;
88
- cleanup?: never;
89
- leak?: never;
90
99
  overlaps?: never;
91
100
  overlapFailure?: never;
92
101
  report?: never;
93
102
  diff?: never;
94
- verificationVerdict?: never;
95
103
  chapter?: never;
96
104
  note?: never;
97
105
  }>;
@@ -1,6 +1,6 @@
1
1
  import { type DerivedFileAction } from "./coordination/durable-file.js";
2
2
  import { type ContractId, type ContractState } from "./core/facts/types.js";
3
- import { type GitRepository } from "./git/repository.js";
3
+ import { type GitRepository } from "./git/process.js";
4
4
  import { type PlaceRegister } from "./workspace-place.js";
5
5
  export declare function renderContractGuidance(state: ContractState): string;
6
6
  export type ContractFileEffect = Readonly<{
@@ -40,5 +40,4 @@ export type ContractReservation = Readonly<{
40
40
  export declare function reserveContractWorktree(repository: GitRepository, contract: ContractId): Promise<ContractReservation>;
41
41
  export declare function withContractWorktreeAppointment<T>(repository: GitRepository, action: () => T | Promise<T>): Promise<T>;
42
42
  export declare function releaseContractWorktree(repository: GitRepository, contract: ContractId): Promise<void>;
43
- export declare function removeContractWorktreeAppointment(repository: GitRepository, contract: ContractId): Promise<void>;
44
43
  export declare function projectContractWorktree(repository: GitRepository, state: ContractState | null, register?: PlaceRegister): Promise<ContractWorktreeResult>;
@@ -3,9 +3,10 @@ import { join } from "node:path";
3
3
  import { acquireSqliteTransactionLock } from "./coordination/sqlite-transaction-lock.js";
4
4
  import { createFileDurablyExclusive, repairDerivedFile, } from "./coordination/durable-file.js";
5
5
  import { contractId } from "./core/facts/types.js";
6
- import { GitPlumbingError, runGit, worktreeGitDirectory, worktreeRoot, } from "./git/repository.js";
6
+ import { worktreeGitDirectory, worktreeRoot, } from "./git/repository.js";
7
+ import { GitPlumbingError, runGit } from "./git/process.js";
7
8
  import { worktreePath } from "./git/workspace.js";
8
- import { appointmentFor, placeRegisterPath, readPlaceRegister, } from "./workspace-place.js";
9
+ import { appointmentFor, placeRegisterPath, } from "./workspace-place.js";
9
10
  const IGNORE_BYTES = ".gitignore\nKEIYAKU.md\n";
10
11
  const APPOINTMENT_DESCRIPTION = "This is a read-only projection. Do not edit manually.";
11
12
  function appointmentBytes(contract) {
@@ -132,12 +133,6 @@ export async function releaseContractWorktree(repository, contract) {
132
133
  }
133
134
  catch { /* reservation cleanup is best effort */ }
134
135
  }
135
- export async function removeContractWorktreeAppointment(repository, contract) {
136
- const appointment = await readContractAppointment(repository);
137
- if (appointment.kind !== "appointed" || appointment.contract !== contract)
138
- return;
139
- await unlink(appointment.path);
140
- }
141
136
  async function repair(repository, worktree, relativePath, bytes) {
142
137
  const path = join(worktree, relativePath);
143
138
  if (await isTracked(repository, relativePath))
@@ -238,7 +233,7 @@ export async function projectContractWorktree(repository, state, register) {
238
233
  if (state.coordinates.workspace === "here") {
239
234
  return await here(repository, state, renderContractGuidance(state));
240
235
  }
241
- const appointed = appointmentFor(register ?? await readPlaceRegister(repository), state.id);
236
+ const appointed = register === undefined ? undefined : appointmentFor(register, state.id);
242
237
  if (appointed === undefined) {
243
238
  return {
244
239
  effects: [],
@@ -6,6 +6,7 @@ const VERSION_BY_KIND = {
6
6
  amend: 1,
7
7
  bound: 1,
8
8
  deliver: 1,
9
+ reintegrated: 1,
9
10
  attestation: 1,
10
11
  claimed: 1,
11
12
  arc: 1,
@@ -182,6 +183,14 @@ function validateData(kind, value) {
182
183
  },
183
184
  };
184
185
  }
186
+ case "reintegrated": {
187
+ const object = requireRecord(value, path);
188
+ requireKeys(object, ["predecessor", "snapshot"], path);
189
+ return {
190
+ predecessor: brandedValue(object.predecessor, `${path}.predecessor`, snapshotId),
191
+ snapshot: brandedValue(object.snapshot, `${path}.snapshot`, snapshotId),
192
+ };
193
+ }
185
194
  case "attestation":
186
195
  return validateAttestation(value, path);
187
196
  case "claimed": {
@@ -24,6 +24,7 @@ function stateFromBind(id, head, bind, attestations) {
24
24
  terms: cloneTerms(bind.data.terms),
25
25
  bound: null,
26
26
  delivery: null,
27
+ currentIntegration: null,
27
28
  attestations,
28
29
  terminal: null,
29
30
  };
@@ -55,7 +56,18 @@ function foldEntry(state, entry, attestations) {
55
56
  case "deliver":
56
57
  if (state.bound === null)
57
58
  foldError("deliver requires bound");
58
- return { ...state, delivery: entry };
59
+ return { ...state, delivery: entry, currentIntegration: { ...entry.data.integration } };
60
+ case "reintegrated":
61
+ if (state.delivery === null)
62
+ foldError("reintegrated requires a deliver");
63
+ return {
64
+ ...state,
65
+ currentIntegration: {
66
+ predecessor: entry.data.predecessor,
67
+ snapshot: entry.data.snapshot,
68
+ changeId: state.delivery.data.integration.changeId,
69
+ },
70
+ };
59
71
  case "attestation":
60
72
  attestations.push(entry);
61
73
  return { ...state, attestations };
@@ -85,6 +85,11 @@ export type DeliverData = Readonly<{
85
85
  requireBranchesToBeUpToDate: boolean;
86
86
  }>;
87
87
  }>;
88
+ export type CurrentIntegration = DeliverData["integration"];
89
+ export type ReintegratedData = Readonly<{
90
+ predecessor: SnapshotId;
91
+ snapshot: SnapshotId;
92
+ }>;
88
93
  export type AttestationData = Readonly<{
89
94
  gate: Gate;
90
95
  subject: DependencyKeySet;
@@ -116,11 +121,12 @@ export type BindEntry = JournalEnvelope<"bind", BindData>;
116
121
  type AmendEntry = JournalEnvelope<"amend", AmendData>;
117
122
  type BoundEntry = JournalEnvelope<"bound", BoundData>;
118
123
  type DeliverEntry = JournalEnvelope<"deliver", DeliverData>;
124
+ export type ReintegratedEntry = JournalEnvelope<"reintegrated", ReintegratedData>;
119
125
  export type AttestationEntry = JournalEnvelope<"attestation", AttestationData>;
120
126
  type ClaimedEntry = JournalEnvelope<"claimed", ClaimedData>;
121
127
  export type ArcEntry = JournalEnvelope<"arc", ArcData>;
122
128
  type AbandonedEntry = JournalEnvelope<"abandoned", AbandonedData>;
123
- export type JournalEntry = BindEntry | AmendEntry | BoundEntry | DeliverEntry | AttestationEntry | ClaimedEntry | ArcEntry | AbandonedEntry;
129
+ export type JournalEntry = BindEntry | AmendEntry | BoundEntry | DeliverEntry | ReintegratedEntry | AttestationEntry | ClaimedEntry | ArcEntry | AbandonedEntry;
124
130
  export type FactKind = JournalEntry["kind"];
125
131
  type ContractTerminal = ClaimedEntry | AbandonedEntry;
126
132
  export type ContractState = Readonly<{
@@ -130,6 +136,7 @@ export type ContractState = Readonly<{
130
136
  terms: ContractTerms;
131
137
  bound: BoundEntry | null;
132
138
  delivery: DeliverEntry | null;
139
+ currentIntegration: CurrentIntegration | null;
133
140
  attestations: readonly AttestationEntry[];
134
141
  currentArc?: ArcEntry;
135
142
  terminal: ContractTerminal | null;
@@ -65,12 +65,13 @@ export function parseDependencyKeySet(value) {
65
65
  return decodeDependencyKeySet(value).canonical;
66
66
  }
67
67
  function currentKeys(state) {
68
+ const integration = state.currentIntegration ?? state.delivery?.data.integration;
68
69
  return new Set([
69
70
  encodeKey({ kind: "document", value: state.terms.document.key }),
70
71
  ...state.terms.segments.map((value) => encodeKey({ kind: "segment", value })),
71
- ...(state.delivery === null ? [] : [
72
- encodeKey({ kind: "snapshot", value: state.delivery.data.integration.snapshot }),
73
- encodeKey({ kind: "change", value: state.delivery.data.integration.changeId }),
72
+ ...(integration === undefined ? [] : [
73
+ encodeKey({ kind: "snapshot", value: integration.snapshot }),
74
+ encodeKey({ kind: "change", value: integration.changeId }),
74
75
  ]),
75
76
  ]);
76
77
  }
@@ -5,9 +5,17 @@ type PlacementInput = Readonly<{
5
5
  actor?: ActorId;
6
6
  at: string;
7
7
  }>;
8
+ export type UnmetPrerequisite = Readonly<{
9
+ contractId: ContractId;
10
+ state: "missing" | "active" | "abandoned";
11
+ }>;
8
12
  export type PlacementRefusal = Readonly<{
9
- kind: "contract-missing" | "delivery-missing" | "terminal" | "prerequisites-unsatisfied" | "gates-unsatisfied";
13
+ kind: "contract-missing" | "delivery-missing" | "terminal" | "gates-unsatisfied";
14
+ contractId: ContractId;
15
+ }> | Readonly<{
16
+ kind: "prerequisites-unsatisfied";
10
17
  contractId: ContractId;
18
+ unmet: readonly UnmetPrerequisite[];
11
19
  }>;
12
20
  export declare function decidePlacement({ input, attempt, observation }: DecideInput<PlacementInput>): OfferDecision<PlacementRefusal>;
13
21
  export {};