@astrosheep/keiyaku 4.5.19 → 4.5.21

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 (197) hide show
  1. package/README.md +4 -4
  2. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +14 -7
  3. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +53 -20
  4. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/references/automation.md +237 -0
  5. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +6 -7
  6. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +2 -4
  7. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +3 -3
  8. package/build/src/akuma/akuma-handle.d.ts +5 -1
  9. package/build/src/akuma/akuma-handle.js +4 -1
  10. package/build/src/akuma/akuma-instance.d.ts +7 -2
  11. package/build/src/akuma/akuma-instance.js +14 -7
  12. package/build/src/akuma/akuma-observe.d.ts +11 -0
  13. package/build/src/akuma/akuma-observe.js +23 -0
  14. package/build/src/akuma/akuma-product.d.ts +2 -0
  15. package/build/src/akuma/akuma-product.js +6 -0
  16. package/build/src/akuma/akuma.d.ts +20 -1
  17. package/build/src/akuma/akuma.js +2 -0
  18. package/build/src/akuma/allowed.d.ts +20 -0
  19. package/build/src/akuma/allowed.js +11 -0
  20. package/build/src/akuma/archetype.js +3 -0
  21. package/build/src/akuma/body.d.ts +25 -5
  22. package/build/src/akuma/body.js +70 -31
  23. package/build/src/akuma/call-request.d.ts +5 -0
  24. package/build/src/akuma/call-request.js +14 -4
  25. package/build/src/akuma/fleet-execution.d.ts +28 -0
  26. package/build/src/akuma/fleet-execution.js +36 -4
  27. package/build/src/akuma/fleet-observation.d.ts +54 -0
  28. package/build/src/akuma/fleet-request.d.ts +10 -0
  29. package/build/src/akuma/fleet-request.js +13 -3
  30. package/build/src/akuma/heart/facts.d.ts +2 -0
  31. package/build/src/akuma/heart/index.d.ts +1 -0
  32. package/build/src/akuma/heart/index.js +4 -1
  33. package/build/src/akuma/heart/rows.d.ts +2 -0
  34. package/build/src/akuma/heart/rows.js +7 -5
  35. package/build/src/akuma/heart/schema.d.ts +1 -1
  36. package/build/src/akuma/heart/schema.js +3 -1
  37. package/build/src/akuma/heart/tells.js +6 -5
  38. package/build/src/akuma/heart/timeline.js +1 -1
  39. package/build/src/akuma/provider-recipe.d.ts +1 -0
  40. package/build/src/akuma/provider-recipe.js +5 -0
  41. package/build/src/akuma/provider.d.ts +1 -1
  42. package/build/src/akuma/provider.js +1 -1
  43. package/build/src/akuma/providers/acp/core.js +3 -6
  44. package/build/src/akuma/providers/acp/index.js +3 -0
  45. package/build/src/akuma/providers/claude/index.js +10 -10
  46. package/build/src/akuma/providers/codex-app-server/index.js +12 -11
  47. package/build/src/akuma/providers/execution-environment.d.ts +6 -0
  48. package/build/src/akuma/providers/execution-environment.js +24 -0
  49. package/build/src/akuma/providers/grok-build/index.js +3 -0
  50. package/build/src/akuma/providers/opencode-sdk/index.js +32 -23
  51. package/build/src/akuma/providers/opencode-sdk/session.d.ts +8 -1
  52. package/build/src/akuma/providers/opencode-sdk/session.js +6 -5
  53. package/build/src/akuma/providers/pi/events.js +7 -2
  54. package/build/src/akuma/providers/pi/index.js +5 -2
  55. package/build/src/akuma/request-observation.d.ts +45 -0
  56. package/build/src/akuma/request-observation.js +162 -0
  57. package/build/src/akuma/request-rendezvous.d.ts +6 -3
  58. package/build/src/akuma/request-rendezvous.js +80 -14
  59. package/build/src/akuma/request-serve.js +27 -4
  60. package/build/src/akuma/request-wire.d.ts +5 -0
  61. package/build/src/akuma/request-wire.js +2 -0
  62. package/build/src/akuma/turn-drive.d.ts +1 -0
  63. package/build/src/akuma/turn-drive.js +2 -0
  64. package/build/src/akuma-body.js +5 -0
  65. package/build/src/body/amend.js +6 -4
  66. package/build/src/body/decode.d.ts +3 -1
  67. package/build/src/body/decode.js +53 -17
  68. package/build/src/body/region.d.ts +9 -1
  69. package/build/src/body/region.js +87 -12
  70. package/build/src/body/render.js +1 -1
  71. package/build/src/body/verification.d.ts +3 -1
  72. package/build/src/body/verification.js +4 -1
  73. package/build/src/cli/accepted.js +19 -2
  74. package/build/src/cli/commands/akuma-invoke.d.ts +3 -1
  75. package/build/src/cli/commands/akuma-invoke.js +134 -14
  76. package/build/src/cli/commands/akuma.js +24 -12
  77. package/build/src/cli/commands/contract-help.d.ts +15 -15
  78. package/build/src/cli/commands/contract-help.js +45 -19
  79. package/build/src/cli/commands/contract-invoke.d.ts +3 -0
  80. package/build/src/cli/commands/contract-invoke.js +25 -11
  81. package/build/src/cli/commands/contract.js +6 -4
  82. package/build/src/cli/commands/install.d.ts +1 -1
  83. package/build/src/cli/commands/install.js +1 -1
  84. package/build/src/cli/commands/status-set.js +15 -24
  85. package/build/src/cli/commands/task.js +18 -18
  86. package/build/src/cli/invoke.d.ts +3 -0
  87. package/build/src/cli/invoke.js +58 -23
  88. package/build/src/cli/main.js +5 -0
  89. package/build/src/cli/parse.d.ts +3 -0
  90. package/build/src/cli/parse.js +26 -4
  91. package/build/src/cli/render/akuma-activity.d.ts +100 -2
  92. package/build/src/cli/render/akuma-activity.js +485 -40
  93. package/build/src/cli/render/akuma.d.ts +10 -0
  94. package/build/src/cli/render/akuma.js +31 -21
  95. package/build/src/cli/render/audit.js +1 -1
  96. package/build/src/cli/render/board.js +1 -1
  97. package/build/src/cli/render/catalog.js +16 -13
  98. package/build/src/cli/render/contract-history.js +39 -10
  99. package/build/src/cli/render/contract-observation.js +10 -6
  100. package/build/src/cli/render/contract.js +109 -106
  101. package/build/src/cli/render/execution-progress.d.ts +26 -0
  102. package/build/src/cli/render/execution-progress.js +178 -0
  103. package/build/src/cli/render/kanshi-akuma.js +27 -39
  104. package/build/src/cli/render/kanshi.js +33 -26
  105. package/build/src/cli/render/nuke.js +1 -1
  106. package/build/src/cli/render/receipt.d.ts +1 -1
  107. package/build/src/cli/render/receipt.js +6 -6
  108. package/build/src/cli/render/refusal.js +10 -7
  109. package/build/src/cli/render/region.js +1 -1
  110. package/build/src/cli/render/settings.js +12 -2
  111. package/build/src/cli/render/status-line.d.ts +30 -0
  112. package/build/src/cli/render/status-line.js +58 -0
  113. package/build/src/cli/render/status-set.js +1 -1
  114. package/build/src/cli/render/task.d.ts +2 -0
  115. package/build/src/cli/render/task.js +11 -7
  116. package/build/src/cli/render/terminal.d.ts +0 -1
  117. package/build/src/cli/render/terminal.js +47 -15
  118. package/build/src/cli/result.d.ts +1 -0
  119. package/build/src/cli/runtime.d.ts +2 -0
  120. package/build/src/cli/runtime.js +36 -4
  121. package/build/src/cli/usage.js +2 -2
  122. package/build/src/cli/version.d.ts +1 -0
  123. package/build/src/cli/version.js +25 -0
  124. package/build/src/git/hooks.d.ts +13 -1
  125. package/build/src/git/hooks.js +2 -1
  126. package/build/src/git/workspace.d.ts +1 -1
  127. package/build/src/git/workspace.js +1 -1
  128. package/build/src/identity/mint.d.ts +13 -0
  129. package/build/src/identity/mint.js +26 -0
  130. package/build/src/identity/normalize.d.ts +4 -0
  131. package/build/src/identity/normalize.js +30 -0
  132. package/build/src/index.d.ts +2 -0
  133. package/build/src/index.js +1 -0
  134. package/build/src/kanshi/read.js +17 -4
  135. package/build/src/kanshi/report.d.ts +1 -0
  136. package/build/src/library/akuma-creation.d.ts +2 -0
  137. package/build/src/library/akuma-creation.js +34 -18
  138. package/build/src/library/audit.d.ts +2 -0
  139. package/build/src/library/audit.js +1 -0
  140. package/build/src/library/bind.js +18 -16
  141. package/build/src/library/composition.d.ts +1 -0
  142. package/build/src/library/contract-bind.d.ts +3 -1
  143. package/build/src/library/contract-bind.js +5 -3
  144. package/build/src/library/contract-execution.d.ts +2 -0
  145. package/build/src/library/contract-execution.js +15 -1
  146. package/build/src/library/contract-forwarding.d.ts +4 -0
  147. package/build/src/library/contract-forwarding.js +3 -0
  148. package/build/src/library/contract-handle.d.ts +7 -0
  149. package/build/src/library/contract-handle.js +25 -0
  150. package/build/src/library/contract-operations.d.ts +7 -2
  151. package/build/src/library/contract-operations.js +21 -1
  152. package/build/src/library/contract-types.d.ts +1 -0
  153. package/build/src/library/execution-result.d.ts +2 -2
  154. package/build/src/library/execution.d.ts +8 -0
  155. package/build/src/library/execution.js +102 -0
  156. package/build/src/library/fleet.d.ts +4 -1
  157. package/build/src/library/fleet.js +32 -8
  158. package/build/src/library/keiyaku.d.ts +2 -0
  159. package/build/src/library/mutation.js +15 -7
  160. package/build/src/plugin/akuma-signals.d.ts +6 -0
  161. package/build/src/plugin/akuma-signals.js +9 -1
  162. package/build/src/plugin/public.d.ts +6 -2
  163. package/build/src/plugin/runtime.d.ts +1 -3
  164. package/build/src/plugin/runtime.js +95 -103
  165. package/build/src/protocol/amend.js +4 -3
  166. package/build/src/protocol/attempt.d.ts +2 -0
  167. package/build/src/protocol/bind.js +41 -14
  168. package/build/src/protocol/completion.d.ts +3 -0
  169. package/build/src/protocol/completion.js +35 -11
  170. package/build/src/protocol/deliver.js +165 -92
  171. package/build/src/protocol/execution-observation.d.ts +181 -0
  172. package/build/src/protocol/execution-observation.js +45 -0
  173. package/build/src/protocol/intent.d.ts +18 -22
  174. package/build/src/protocol/intent.js +63 -41
  175. package/build/src/protocol/operations.d.ts +2 -0
  176. package/build/src/protocol/placement.js +5 -1
  177. package/build/src/protocol/progress.d.ts +5 -1
  178. package/build/src/protocol/progress.js +10 -1
  179. package/build/src/protocol/read/status.d.ts +1 -0
  180. package/build/src/protocol/read/status.js +10 -2
  181. package/build/src/protocol/reintegrate.d.ts +1 -1
  182. package/build/src/protocol/reintegrate.js +70 -43
  183. package/build/src/protocol/result-codec.js +27 -11
  184. package/build/src/protocol/review.js +53 -30
  185. package/build/src/protocol/run.d.ts +83 -36
  186. package/build/src/protocol/run.js +90 -75
  187. package/build/src/runtime/proc/run.d.ts +16 -4
  188. package/build/src/runtime/proc/run.js +64 -22
  189. package/build/src/runtime/proc/windows-launch.exe +0 -0
  190. package/build/src/settings.js +5 -14
  191. package/build/src/task/document.d.ts +5 -1
  192. package/build/src/task/document.js +6 -1
  193. package/build/src/verification/execution.d.ts +10 -2
  194. package/build/src/verification/execution.js +143 -42
  195. package/build/src/verification/observation.d.ts +41 -0
  196. package/build/src/verification/observation.js +29 -0
  197. package/package.json +3 -1
@@ -3,32 +3,54 @@ import { projectSettings } from "../settings.js";
3
3
  import { dependencyKeySet } from "../core/subject.js";
4
4
  import { latestCurrentAttestations } from "../core/facts/gate.js";
5
5
  import { decideAttestation } from "../core/verbs/attestation.js";
6
- import { executeVerification, } from "../verification/execution.js";
6
+ import { executeVerification, capturedOutput, } from "../verification/execution.js";
7
7
  import { VERIFIED } from "../verification/declaration.js";
8
8
  import { runProtocol } from "./run.js";
9
9
  import { mintAttempts } from "./attempt.js";
10
- /** Observe, decide, and atomically admit one intent with bounded Git retries. */
10
+ function admissionOptions(options) {
11
+ return {
12
+ ...(options.progress === undefined ? {} : { progress: options.progress }),
13
+ ...(options.observedContracts === undefined ? {} : { observedContracts: options.observedContracts }),
14
+ ...(options.observe === undefined ? {} : { observe: options.observe }),
15
+ ...(options.decorateOffer === undefined ? {} : { decorateOffer: options.decorateOffer }),
16
+ ...(options.validateAdmission === undefined ? {} : { validateAdmission: options.validateAdmission }),
17
+ ...(options.observationSelection === undefined ? {} : { observationSelection: options.observationSelection }),
18
+ };
19
+ }
20
+ /** Observe, decide, and atomically admit one intent whose invocation already is its decision input. */
11
21
  export function admitIntent(channel, repository, input, decide, options = {}) {
12
22
  const contracts = options.observedContracts ?? [input.contractId];
13
23
  return runProtocol({
24
+ ...admissionOptions(options),
14
25
  input,
15
26
  channel,
16
27
  repository,
17
28
  contracts,
18
29
  attempts: mintAttempts({ entryCount: 2 }),
19
30
  decide,
20
- ...(options.progress === undefined ? {} : { progress: options.progress }),
21
- ...(options.observe === undefined ? {} : { observe: options.observe }),
22
- ...(options.decorateOffer === undefined ? {} : { decorateOffer: options.decorateOffer }),
23
- ...(options.validateAdmission === undefined ? {} : { validateAdmission: options.validateAdmission }),
24
- ...(options.observationSelection === undefined ? {} : { observationSelection: options.observationSelection }),
25
- ...(options.prepareInput === undefined ? {} : { prepareInput: options.prepareInput }),
31
+ });
32
+ }
33
+ /**
34
+ * Observe, decide, and atomically admit one intent whose repeatable preparation happens outside
35
+ * the publication seat and whose decision input is assembled from the fresh in-custody observation.
36
+ */
37
+ export function admitPreparedIntent(channel, repository, input, decide, options) {
38
+ const contracts = options.observedContracts ?? [input.contractId];
39
+ return runProtocol({
40
+ ...admissionOptions(options),
41
+ input,
42
+ preparation: options.preparation,
43
+ channel,
44
+ repository,
45
+ contracts,
46
+ attempts: mintAttempts({ entryCount: 2 }),
47
+ decide,
26
48
  });
27
49
  }
28
50
  function runtimeStop(outcome) {
29
51
  return outcome.kind === "spawn-error"
30
- ? { failure: outcome.kind, diagnostic: outcome.diagnostic }
31
- : { failure: outcome.kind };
52
+ ? { failure: outcome.kind, diagnostic: outcome.diagnostic, ...capturedOutput(outcome) }
53
+ : { failure: outcome.kind, ...capturedOutput(outcome) };
32
54
  }
33
55
  function verificationInput(outcome, input, subject) {
34
56
  return {
@@ -61,6 +83,33 @@ function retainVerificationReceipt(input, step) {
61
83
  if (!("failure" in step) && step.kind === "accepted")
62
84
  input.progress?.recordResidue(input.contractId, step);
63
85
  }
86
+ async function verificationStep(input, execution, subject) {
87
+ if (execution.outcome.kind === "terminal") {
88
+ return await admitIntent(input.channel, input.repository, verificationInput(execution.outcome, input, subject), decideAttestation, input.progress === undefined ? {} : { progress: input.progress });
89
+ }
90
+ if (execution.outcome.kind === "candidate-unavailable")
91
+ return { failure: "candidate-unavailable", diagnostic: execution.outcome.diagnostic };
92
+ if (execution.outcome.kind === "environment-failure") {
93
+ return "diagnostic" in execution.outcome
94
+ ? { failure: "environment-failure", diagnostic: execution.outcome.diagnostic }
95
+ : { failure: "environment-failure", name: execution.outcome.name, detail: execution.outcome.detail };
96
+ }
97
+ if (execution.outcome.kind === "unknown-exit" ||
98
+ execution.outcome.kind === "cancelled" ||
99
+ execution.outcome.kind === "spawn-error")
100
+ return runtimeStop(execution.outcome);
101
+ throw new Error("Verification execution returned an invalid outcome");
102
+ }
103
+ function verificationCounts(execution) {
104
+ if (execution.outcome.kind !== "terminal")
105
+ return undefined;
106
+ return {
107
+ passed: execution.outcome.passed,
108
+ total: execution.outcome.total,
109
+ verdict: execution.outcome.verdict,
110
+ ...(execution.outcome.summary === undefined ? {} : { summary: execution.outcome.summary }),
111
+ };
112
+ }
64
113
  /** Run Verification against an explicit or admitted integration snapshot. */
65
114
  export async function verifyDelivery(input) {
66
115
  const snapshot = input.snapshot ?? input.state.currentIntegration?.snapshot;
@@ -76,43 +125,16 @@ export async function verifyDelivery(input) {
76
125
  declarations: input.verification.declarations,
77
126
  materializeScratchCandidate,
78
127
  projectSettings,
128
+ observe: (observation) => input.progress?.observe({ kind: "verification", contractId: input.contractId, snapshot, observation }),
79
129
  ...(input.signal === undefined ? {} : { signal: input.signal }),
80
130
  });
81
131
  input.progress?.recordVerification(input.contractId, snapshot, execution);
82
- let step;
83
- if (execution.outcome.kind === "terminal") {
84
- step = await admitIntent(input.channel, input.repository, verificationInput(execution.outcome, input, subject), decideAttestation, input.progress === undefined ? {} : { progress: input.progress });
85
- }
86
- else if (execution.outcome.kind === "candidate-unavailable") {
87
- step = { failure: "candidate-unavailable", diagnostic: execution.outcome.diagnostic };
88
- }
89
- else if (execution.outcome.kind === "environment-failure") {
90
- step =
91
- "diagnostic" in execution.outcome
92
- ? { failure: "environment-failure", diagnostic: execution.outcome.diagnostic }
93
- : { failure: "environment-failure", name: execution.outcome.name, detail: execution.outcome.detail };
94
- }
95
- else if (execution.outcome.kind === "unknown-exit" ||
96
- execution.outcome.kind === "cancelled" ||
97
- execution.outcome.kind === "spawn-error") {
98
- step = runtimeStop(execution.outcome);
99
- }
100
- else {
101
- throw new Error("Verification execution returned an invalid outcome");
102
- }
132
+ const step = await verificationStep(input, execution, subject);
133
+ const counts = verificationCounts(execution);
103
134
  retainVerificationReceipt(input, step);
104
135
  return {
105
136
  step,
106
- ...(execution.outcome.kind === "terminal"
107
- ? {
108
- counts: {
109
- passed: execution.outcome.passed,
110
- total: execution.outcome.total,
111
- verdict: execution.outcome.verdict,
112
- ...(execution.outcome.summary === undefined ? {} : { summary: execution.outcome.summary }),
113
- },
114
- }
115
- : {}),
137
+ ...(counts === undefined ? {} : { counts }),
116
138
  ...(execution.cleanup === undefined ? {} : { cleanup: execution.cleanup }),
117
139
  ...(execution.leak === undefined ? {} : { leak: execution.leak }),
118
140
  };
@@ -103,6 +103,8 @@ export type AttemptDecision<Value, Refusal = IntentRefusal> = (AcceptedAdmission
103
103
  refusal: Refusal;
104
104
  }> | Readonly<{
105
105
  kind: "redecide";
106
+ }> | Readonly<{
107
+ kind: "stale";
106
108
  }> | Readonly<{
107
109
  kind: "collision";
108
110
  }> | Extract<DecidedOfferResult, {
@@ -49,7 +49,7 @@ async function runFencedPlacement(repository, input, protocol) {
49
49
  const result = placementResultWithSeatClose(await withPrivateStatePublicationSeat(repository, async (seat) => {
50
50
  // The hint carries no acceptance authority. Form a fresh semantic offer under the seat.
51
51
  const prepared = await prepareProtocolAttempt(protocol, protocol.attempts[index]);
52
- if (prepared.kind === "refused")
52
+ if (prepared.kind !== "offered")
53
53
  return prepared;
54
54
  const state = contractState(prepared.observation.decision, input.contractId);
55
55
  if (state === null)
@@ -68,6 +68,8 @@ async function runFencedPlacement(repository, input, protocol) {
68
68
  observedTreeEqualsCandidate: await observedTreeEqualsCandidate(repository, observed, target.newOid),
69
69
  };
70
70
  if (changes?.predecessor !== target.expectedOid || changes.candidate !== target.newOid)
71
+ // A target preparation computed before custody is spent once the fresh offer names another
72
+ // movement. Re-derive it on the next attempt; never publish a preparation for stale bytes.
71
73
  return { kind: "changed-preparation", target };
72
74
  const physical = await prepareTargetPlacement(repository, state, target, changes);
73
75
  if (physical.kind === "refused")
@@ -92,6 +94,8 @@ async function runFencedPlacement(repository, input, protocol) {
92
94
  }
93
95
  if (result.kind === "redecide")
94
96
  continue;
97
+ if (result.kind === "stale")
98
+ continue;
95
99
  if (result.kind === "collision" && index + 1 < protocol.attempts.length)
96
100
  continue;
97
101
  if (result.kind !== "accepted")
@@ -4,6 +4,7 @@ import type { WorktreeLeak } from "../git/scratch.js";
4
4
  import type { PrivateStateSeatCloseLag } from "../git/private-state-seat.js";
5
5
  import type { VerificationCleanupFailure } from "./intent.js";
6
6
  import type { AcceptedProtocolStep, IntentOutcome } from "./outcome.js";
7
+ import { type ExecutionEvent, type ExecutionObserver } from "./execution-observation.js";
7
8
  /** A captured interpretation is not an invocation's admission receipt. */
8
9
  export type ContractCheckpoint = Readonly<{
9
10
  state: ContractState;
@@ -48,8 +49,11 @@ type Residue = Readonly<{
48
49
  physical?: ReconcileResult;
49
50
  seatClose?: readonly PrivateStateSeatCloseLag[];
50
51
  }>;
51
- /** Invocation-local receipts only: no persistence, scheduling, callbacks, or authority reads. */
52
+ /** Invocation-local receipts and ephemeral observations; no scheduling or authority reads. */
52
53
  export declare class ExecutionProgress {
54
+ private readonly observer?;
55
+ constructor(observer?: ExecutionObserver | undefined);
56
+ observe(event: ExecutionEvent): void;
53
57
  private readonly entries;
54
58
  private readonly admittedFacts;
55
59
  private readonly admittedHeads;
@@ -2,6 +2,7 @@ import { encodeEntry } from "../core/facts/codec.js";
2
2
  import { AuthorityCorruptionError } from "../core/facts/errors.js";
3
3
  import { GitPlumbingError } from "../git/process.js";
4
4
  import { SqliteTransactionLockError } from "../coordination/sqlite-transaction-lock.js";
5
+ import { observeExecution } from "./execution-observation.js";
5
6
  export function contractCheckpoint(input) {
6
7
  return { state: input.state, journal: input.journal };
7
8
  }
@@ -21,8 +22,15 @@ export function executionStop(contractId, stage, error, signal) {
21
22
  diagnostic: error instanceof Error ? error.message : String(error),
22
23
  };
23
24
  }
24
- /** Invocation-local receipts only: no persistence, scheduling, callbacks, or authority reads. */
25
+ /** Invocation-local receipts and ephemeral observations; no scheduling or authority reads. */
25
26
  export class ExecutionProgress {
27
+ observer;
28
+ constructor(observer) {
29
+ this.observer = observer;
30
+ }
31
+ observe(event) {
32
+ observeExecution(this.observer, event);
33
+ }
26
34
  entries = new Map();
27
35
  admittedFacts = [];
28
36
  admittedHeads = new Map();
@@ -50,6 +58,7 @@ export class ExecutionProgress {
50
58
  continue;
51
59
  this.entries.set(key, bytes);
52
60
  this.admittedFacts.push(fact);
61
+ this.observe({ kind: "admitted", contractId, fact });
53
62
  this.affectedContracts.add(fact.contract);
54
63
  fresh = true;
55
64
  }
@@ -19,6 +19,7 @@ export type ContractVerificationStatus = Readonly<{
19
19
  kind: "recorded";
20
20
  verdict: "satisfied" | "unsatisfied";
21
21
  at: string;
22
+ snapshot?: SnapshotId;
22
23
  }>;
23
24
  export type ContractTargetLag = GitContractTargetLag | Readonly<{
24
25
  kind: "counted";
@@ -1,5 +1,6 @@
1
1
  import { observeActiveContractWorld, observeContractsForAdmissionInObservationAt, observeDeliveryTargetAt, withContractReadObservationAt, } from "../../git/observe.js";
2
2
  import { decodeContractDocument, verificationDefinition } from "../../body/decode.js";
3
+ import { dependencyKeys } from "../../core/subject.js";
3
4
  import { observeTargetLag, observeWorkspace, conflictHandoffFor, conflictRecovery, worktreePath, } from "../../git/workspace.js";
4
5
  import { appointmentFor, readPlaceRegister } from "../../workspace-place.js";
5
6
  import { gateReports, latestCurrentAttestations } from "../../core/facts/gate.js";
@@ -36,8 +37,15 @@ function verificationFor(state, document) {
36
37
  return { kind: "undeclared" };
37
38
  const verifiedGate = gate("verified");
38
39
  const current = latestCurrentAttestations(state, new Set([verifiedGate])).get(verifiedGate);
39
- if (current !== undefined)
40
- return { kind: "recorded", verdict: current.data.verdict, at: current.at };
40
+ if (current !== undefined) {
41
+ const snapshot = dependencyKeys(current.data.subject).find((key) => key.kind === "snapshot")?.value;
42
+ return {
43
+ kind: "recorded",
44
+ verdict: current.data.verdict,
45
+ at: current.at,
46
+ ...(snapshot === undefined ? {} : { snapshot: snapshot }),
47
+ };
48
+ }
41
49
  return state.delivery === null ? undefined : { kind: "unrecorded" };
42
50
  }
43
51
  export function phaseAtFor(state, bindAt) {
@@ -5,7 +5,7 @@ import { type IntegrationPreparationRefusal } from "../git/integration.js";
5
5
  import type { GitRepository } from "../git/process.js";
6
6
  import type { GitDecodeChannel } from "../git/read-observation.js";
7
7
  import { type AcceptedAdmission } from "./attempt.js";
8
- import type { ProtocolTerminal } from "./run.js";
8
+ import { type ProtocolTerminal } from "./run.js";
9
9
  import { type PlacementExecutionFailure } from "./placement.js";
10
10
  type TargetMissing = Readonly<{
11
11
  kind: "target-missing";
@@ -5,6 +5,7 @@ import { observeContractsForAdmissionAt } from "../git/observe.js";
5
5
  import { appendPrivateStateSeatClose, mergePrivateStateSeatClose, withPrivateStatePublicationSeat, } from "../git/private-state-seat.js";
6
6
  import { admitDecidedOffer, mintAttempts } from "./attempt.js";
7
7
  import { timestamp } from "./operations.js";
8
+ import { contractStateWitness, STALE_PRIVATE_STATE_PREPARATION, } from "./run.js";
8
9
  import { runUnderTargetPlacementFence } from "./placement.js";
9
10
  function reintegrationResultWithSeatClose(outcome) {
10
11
  return mergePrivateStateSeatClose(outcome, (value, closeLag) => {
@@ -13,38 +14,59 @@ function reintegrationResultWithSeatClose(outcome) {
13
14
  return { ...value, seatClose: appendPrivateStateSeatClose(value.seatClose, closeLag) };
14
15
  });
15
16
  }
17
+ /** Plan and materialize the target-specific reintegration artifacts outside the publication seat. */
18
+ async function prepareExternalReintegration(input) {
19
+ const observation = await observeContractsForAdmissionAt(input.repository, input.channel, [input.contractId]);
20
+ const state = contractState(observation.decision, input.contractId);
21
+ if (state === null)
22
+ return { kind: "unavailable" };
23
+ return {
24
+ kind: "artifacts",
25
+ witness: contractStateWitness(state),
26
+ artifacts: await planReintegrationArtifacts(input, state),
27
+ };
28
+ }
29
+ async function planReintegrationArtifacts(input, state) {
30
+ if (state.delivery === null || state.currentIntegration === null)
31
+ return { kind: "defer" };
32
+ const planned = await planIntegration(input.repository, { contractId: input.contractId, coordinates: state.coordinates }, await persistedTender(input.repository, state.delivery.data.tenderSnapshot), state.delivery.data.policy.requireBranchesToBeUpToDate);
33
+ if (planned.kind === "refused")
34
+ return { kind: "refused", refusal: planned.refusal };
35
+ const snapshot = await materializeReintegrationSnapshot(input.repository, planned.data.tree, planned.data.predecessor, state.delivery.data.integration.snapshot);
36
+ return {
37
+ kind: "prepared",
38
+ data: { predecessor: planned.data.predecessor, snapshot },
39
+ assertion: { ref: input.target, oid: planned.data.predecessor },
40
+ };
41
+ }
42
+ /** Assemble the decision input from the fresh Contract; spent artifacts restart the whole cycle. */
43
+ function assembleReintegrationAttempt(input, observation, external) {
44
+ const state = contractState(observation.decision, input.contractId);
45
+ if (state === null) {
46
+ return external.kind === "unavailable"
47
+ ? { kind: "unavailable", refusal: { kind: "contract-missing", contractId: input.contractId } }
48
+ : STALE_PRIVATE_STATE_PREPARATION;
49
+ }
50
+ if (external.kind === "unavailable")
51
+ return STALE_PRIVATE_STATE_PREPARATION;
52
+ if (external.witness.head !== state.head)
53
+ return STALE_PRIVATE_STATE_PREPARATION;
54
+ if (state.delivery === null || state.currentIntegration === null) {
55
+ return { kind: "unavailable", refusal: { kind: "delivery-missing", contractId: input.contractId } };
56
+ }
57
+ return external.artifacts.kind === "defer" ? STALE_PRIVATE_STATE_PREPARATION : external.artifacts;
58
+ }
16
59
  async function runReintegration(input) {
17
- return reintegrationResultWithSeatClose(await withPrivateStatePublicationSeat(input.repository, async (seat) => {
18
- const attempts = mintAttempts({ entryCount: 1 });
19
- for (let index = 0; index < attempts.length; index += 1) {
20
- const attempt = attempts[index];
60
+ const attempts = mintAttempts({ entryCount: 1 });
61
+ for (let index = 0; index < attempts.length; index += 1) {
62
+ const attempt = attempts[index];
63
+ const external = await prepareExternalReintegration(input);
64
+ const result = reintegrationResultWithSeatClose(await withPrivateStatePublicationSeat(input.repository, async (seat) => {
21
65
  const observation = await observeContractsForAdmissionAt(input.repository, input.channel, [input.contractId]);
22
- const state = contractState(observation.decision, input.contractId);
23
- let preparation;
24
- let assertions = [];
25
- if (state === null || state.delivery === null || state.currentIntegration === null) {
26
- preparation = {
27
- kind: "refused",
28
- refusal: {
29
- kind: state === null ? "contract-missing" : "delivery-missing",
30
- contractId: input.contractId,
31
- },
32
- };
33
- }
34
- else {
35
- const planned = await planIntegration(input.repository, { contractId: input.contractId, coordinates: state.coordinates }, await persistedTender(input.repository, state.delivery.data.tenderSnapshot), state.delivery.data.policy.requireBranchesToBeUpToDate);
36
- if (planned.kind === "refused") {
37
- preparation = planned;
38
- }
39
- else {
40
- const snapshot = await materializeReintegrationSnapshot(input.repository, planned.data.tree, planned.data.predecessor, state.delivery.data.integration.snapshot);
41
- preparation = {
42
- kind: "prepared",
43
- data: { predecessor: planned.data.predecessor, snapshot },
44
- };
45
- assertions = [{ ref: input.target, oid: planned.data.predecessor }];
46
- }
47
- }
66
+ const assembled = assembleReintegrationAttempt(input, observation, external);
67
+ if (assembled.kind === "stale")
68
+ return assembled;
69
+ const preparation = assembled.kind === "unavailable" ? { kind: "refused", refusal: assembled.refusal } : assembled;
48
70
  const decision = decideReintegrate({
49
71
  input: {
50
72
  contractId: input.contractId,
@@ -57,7 +79,7 @@ async function runReintegration(input) {
57
79
  });
58
80
  if (decision.kind === "refused")
59
81
  return { kind: "refused", refusal: decision.refusal };
60
- const result = await admitDecidedOffer({
82
+ const admitted = await admitDecidedOffer({
61
83
  channel: input.channel,
62
84
  repository: input.repository,
63
85
  seat,
@@ -65,21 +87,26 @@ async function runReintegration(input) {
65
87
  attempt,
66
88
  offer: decision.offer,
67
89
  primaryContract: input.contractId,
68
- ...(input.progress === undefined ? {} : input.progress === undefined ? {} : { progress: input.progress }),
69
- assertions,
90
+ ...(input.progress === undefined ? {} : { progress: input.progress }),
91
+ ...(assembled.kind === "prepared" ? { assertions: [assembled.assertion] } : {}),
70
92
  });
71
- if (result.kind === "accepted") {
72
- if (preparation.kind !== "prepared")
93
+ if (admitted.kind === "accepted") {
94
+ if (assembled.kind !== "prepared")
73
95
  throw new Error("accepted reintegration has no preparation");
74
- return { ...result, value: preparation.data };
96
+ return { ...admitted, value: assembled.data };
75
97
  }
76
- if (result.kind === "publication-failed")
77
- return { kind: "retry", reason: result };
78
- if (result.kind === "collision" && index + 1 === attempts.length)
79
- return { kind: "retry", reason: result };
80
- }
81
- return { kind: "retry", reason: { kind: "exhausted" } };
82
- }));
98
+ return admitted;
99
+ }));
100
+ if (result.kind === "accepted" || result.kind === "refused")
101
+ return result;
102
+ if (result.kind === "stale" || result.kind === "redecide")
103
+ continue;
104
+ if (result.kind === "publication-failed")
105
+ return { kind: "retry", reason: result };
106
+ if (result.kind === "collision" && index + 1 === attempts.length)
107
+ return { kind: "retry", reason: result };
108
+ }
109
+ return { kind: "retry", reason: { kind: "exhausted" } };
83
110
  }
84
111
  export async function reintegrateOperation(input) {
85
112
  return await runUnderTargetPlacementFence(input.repository, input.target, async () => await runReintegration(input));
@@ -31,6 +31,11 @@ function nonblank(value) {
31
31
  fail();
32
32
  return value;
33
33
  }
34
+ function nonempty(value) {
35
+ if (typeof value !== "string" || value.length === 0)
36
+ fail();
37
+ return value;
38
+ }
34
39
  function integer(value) {
35
40
  if (typeof value !== "number" || !Number.isInteger(value))
36
41
  fail();
@@ -104,10 +109,10 @@ export function decodeDeliverConflictRefusal(value) {
104
109
  const object = record(value, ["kind", "contractId", "reason", "targetHead", "conflictPaths", "recovery"]);
105
110
  if (object.kind !== "integration-failed" || object.reason !== "conflict")
106
111
  fail();
107
- const recovery = record(object.recovery, ["materialize", "continue", "staging"]);
112
+ const recovery = record(object.recovery, ["materialize", "deliver", "staging"]);
108
113
  if (recovery.materialize !== "deliver --materialize-conflict --include-dirty")
109
114
  fail();
110
- if (recovery.continue !== "deliver --include-dirty")
115
+ if (recovery.deliver !== "deliver --include-dirty")
111
116
  fail();
112
117
  if (recovery.staging !== "not-required")
113
118
  fail();
@@ -119,7 +124,7 @@ export function decodeDeliverConflictRefusal(value) {
119
124
  conflictPaths: strings(object.conflictPaths),
120
125
  recovery: {
121
126
  materialize: "deliver --materialize-conflict --include-dirty",
122
- continue: "deliver --include-dirty",
127
+ deliver: "deliver --include-dirty",
123
128
  staging: "not-required",
124
129
  },
125
130
  };
@@ -157,17 +162,25 @@ export function decodeIntentRefusal(value) {
157
162
  decodeVerificationDeclarationRefusal,
158
163
  ]);
159
164
  }
165
+ function decodeCapturedVerificationOutput(object) {
166
+ return {
167
+ ...("stdout" in object ? { stdout: nonempty(object.stdout) } : {}),
168
+ ...("stderr" in object ? { stderr: nonempty(object.stderr) } : {}),
169
+ ...("truncated" in object ? (object.truncated === true ? { truncated: true } : fail()) : {}),
170
+ };
171
+ }
160
172
  export function decodeVerificationRuntimeStop(value) {
161
- const object = record(value, ["failure"], ["diagnostic", "name", "detail"]);
173
+ const object = record(value, ["failure"], ["diagnostic", "name", "detail", "stdout", "stderr", "truncated"]);
174
+ const output = decodeCapturedVerificationOutput(object);
162
175
  if (object.failure === "unknown-exit" || object.failure === "cancelled") {
163
176
  if ("diagnostic" in object || "name" in object || "detail" in object)
164
177
  fail();
165
- return { failure: object.failure };
178
+ return { failure: object.failure, ...output };
166
179
  }
167
180
  if (object.failure === "candidate-unavailable" || object.failure === "spawn-error") {
168
181
  if ("name" in object || "detail" in object)
169
182
  fail();
170
- return { failure: object.failure, diagnostic: nonblank(object.diagnostic) };
183
+ return { failure: object.failure, diagnostic: nonblank(object.diagnostic), ...output };
171
184
  }
172
185
  if (object.failure !== "environment-failure")
173
186
  fail();
@@ -178,9 +191,10 @@ export function decodeVerificationRuntimeStop(value) {
178
191
  failure: "environment-failure",
179
192
  name: nonblank(object.name),
180
193
  detail: decodeHookFailure(object.detail),
194
+ ...output,
181
195
  };
182
196
  }
183
- return { failure: "environment-failure", diagnostic: nonblank(object.diagnostic) };
197
+ return { failure: "environment-failure", diagnostic: nonblank(object.diagnostic), ...output };
184
198
  }
185
199
  function decodeVerificationStepRefusal(value) {
186
200
  const object = record(value, ["refusal"]);
@@ -285,9 +299,11 @@ export function decodeVerificationCleanupFailure(value) {
285
299
  return { phase: "destroy", name: nonblank(object.name), detail: decodeHookFailure(object.detail) };
286
300
  }
287
301
  export function decodeCandidateCompletion(value) {
288
- const object = record(value, ["integration"], ["verification"]);
302
+ const object = record(value, ["integration"], ["predecessor", "target", "verification"]);
289
303
  const completion = {
290
304
  integration: decodeSnapshotId(object.integration),
305
+ ...(object.predecessor === undefined ? {} : { predecessor: decodeSnapshotId(object.predecessor) }),
306
+ ...(object.target === undefined ? {} : { target: nonblank(object.target) }),
291
307
  ...(object.verification === undefined
292
308
  ? {}
293
309
  : {
@@ -325,10 +341,10 @@ export function decodeMaterializedConflict(value) {
325
341
  const object = record(value, ["kind", "targetHead", "conflictPaths", "workspace", "handoffBase", "recovery"]);
326
342
  if (object.kind !== "integration-conflict-materialized")
327
343
  fail();
328
- const recovery = record(object.recovery, ["materialize", "continue", "staging"]);
344
+ const recovery = record(object.recovery, ["materialize", "deliver", "staging"]);
329
345
  if (recovery.materialize !== "deliver --materialize-conflict --include-dirty")
330
346
  fail();
331
- if (recovery.continue !== "deliver --include-dirty")
347
+ if (recovery.deliver !== "deliver --include-dirty")
332
348
  fail();
333
349
  if (recovery.staging !== "not-required")
334
350
  fail();
@@ -340,7 +356,7 @@ export function decodeMaterializedConflict(value) {
340
356
  handoffBase: decodeSnapshotId(object.handoffBase),
341
357
  recovery: {
342
358
  materialize: "deliver --materialize-conflict --include-dirty",
343
- continue: "deliver --include-dirty",
359
+ deliver: "deliver --include-dirty",
344
360
  staging: "not-required",
345
361
  },
346
362
  };