@astrosheep/keiyaku 4.5.20 → 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 (154) hide show
  1. package/README.md +4 -4
  2. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +3 -3
  3. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +22 -8
  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 -3
  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 +22 -0
  14. package/build/src/akuma/akuma-product.d.ts +1 -0
  15. package/build/src/akuma/akuma-product.js +3 -0
  16. package/build/src/akuma/akuma.d.ts +1 -0
  17. package/build/src/akuma/body.d.ts +25 -5
  18. package/build/src/akuma/body.js +65 -8
  19. package/build/src/akuma/call-request.d.ts +2 -0
  20. package/build/src/akuma/call-request.js +2 -0
  21. package/build/src/akuma/fleet-execution.d.ts +28 -0
  22. package/build/src/akuma/fleet-execution.js +36 -4
  23. package/build/src/akuma/fleet-request.d.ts +10 -0
  24. package/build/src/akuma/fleet-request.js +11 -2
  25. package/build/src/akuma/heart/facts.d.ts +2 -0
  26. package/build/src/akuma/heart/index.d.ts +1 -0
  27. package/build/src/akuma/heart/index.js +4 -1
  28. package/build/src/akuma/heart/rows.d.ts +2 -0
  29. package/build/src/akuma/heart/rows.js +7 -5
  30. package/build/src/akuma/heart/schema.d.ts +1 -1
  31. package/build/src/akuma/heart/schema.js +3 -1
  32. package/build/src/akuma/heart/tells.js +6 -5
  33. package/build/src/akuma/heart/timeline.js +1 -1
  34. package/build/src/akuma/provider.d.ts +1 -1
  35. package/build/src/akuma/provider.js +1 -1
  36. package/build/src/akuma/providers/acp/core.js +3 -6
  37. package/build/src/akuma/providers/claude/index.js +9 -12
  38. package/build/src/akuma/providers/codex-app-server/index.js +4 -11
  39. package/build/src/akuma/providers/execution-environment.d.ts +6 -0
  40. package/build/src/akuma/providers/execution-environment.js +24 -0
  41. package/build/src/akuma/providers/opencode-sdk/index.js +30 -23
  42. package/build/src/akuma/providers/opencode-sdk/session.d.ts +8 -1
  43. package/build/src/akuma/providers/opencode-sdk/session.js +6 -5
  44. package/build/src/akuma/providers/pi/events.js +7 -2
  45. package/build/src/akuma/providers/pi/index.js +3 -2
  46. package/build/src/akuma/request-rendezvous.js +8 -3
  47. package/build/src/akuma/turn-drive.d.ts +1 -0
  48. package/build/src/akuma/turn-drive.js +2 -0
  49. package/build/src/akuma-body.js +2 -0
  50. package/build/src/body/amend.js +6 -4
  51. package/build/src/body/decode.d.ts +3 -1
  52. package/build/src/body/decode.js +53 -17
  53. package/build/src/body/region.d.ts +9 -1
  54. package/build/src/body/region.js +87 -12
  55. package/build/src/body/render.js +1 -1
  56. package/build/src/body/verification.d.ts +3 -1
  57. package/build/src/body/verification.js +4 -1
  58. package/build/src/cli/accepted.js +19 -2
  59. package/build/src/cli/commands/akuma-invoke.d.ts +2 -0
  60. package/build/src/cli/commands/akuma-invoke.js +133 -13
  61. package/build/src/cli/commands/akuma.js +11 -4
  62. package/build/src/cli/commands/contract-help.js +17 -4
  63. package/build/src/cli/commands/contract.js +6 -4
  64. package/build/src/cli/commands/status-set.js +15 -24
  65. package/build/src/cli/main.js +5 -0
  66. package/build/src/cli/parse.d.ts +2 -0
  67. package/build/src/cli/parse.js +5 -0
  68. package/build/src/cli/render/akuma-activity.d.ts +99 -2
  69. package/build/src/cli/render/akuma-activity.js +484 -40
  70. package/build/src/cli/render/akuma.d.ts +10 -0
  71. package/build/src/cli/render/akuma.js +28 -21
  72. package/build/src/cli/render/audit.js +1 -1
  73. package/build/src/cli/render/board.js +1 -1
  74. package/build/src/cli/render/catalog.js +16 -13
  75. package/build/src/cli/render/contract-history.js +39 -10
  76. package/build/src/cli/render/contract-observation.js +10 -6
  77. package/build/src/cli/render/contract.js +109 -106
  78. package/build/src/cli/render/execution-progress.d.ts +23 -1
  79. package/build/src/cli/render/execution-progress.js +151 -28
  80. package/build/src/cli/render/kanshi-akuma.js +27 -39
  81. package/build/src/cli/render/kanshi.js +33 -26
  82. package/build/src/cli/render/nuke.js +1 -1
  83. package/build/src/cli/render/receipt.d.ts +1 -1
  84. package/build/src/cli/render/receipt.js +5 -5
  85. package/build/src/cli/render/refusal.js +10 -7
  86. package/build/src/cli/render/region.js +1 -1
  87. package/build/src/cli/render/settings.js +12 -2
  88. package/build/src/cli/render/status-line.d.ts +30 -0
  89. package/build/src/cli/render/status-line.js +58 -0
  90. package/build/src/cli/render/status-set.js +1 -1
  91. package/build/src/cli/render/task.d.ts +2 -0
  92. package/build/src/cli/render/task.js +11 -7
  93. package/build/src/cli/render/terminal.d.ts +0 -1
  94. package/build/src/cli/render/terminal.js +47 -15
  95. package/build/src/cli/result.d.ts +1 -0
  96. package/build/src/cli/runtime.d.ts +2 -0
  97. package/build/src/cli/runtime.js +13 -7
  98. package/build/src/cli/usage.js +2 -2
  99. package/build/src/cli/version.d.ts +1 -0
  100. package/build/src/cli/version.js +25 -0
  101. package/build/src/git/workspace.d.ts +1 -1
  102. package/build/src/git/workspace.js +1 -1
  103. package/build/src/identity/mint.d.ts +13 -0
  104. package/build/src/identity/mint.js +26 -0
  105. package/build/src/identity/normalize.d.ts +4 -0
  106. package/build/src/identity/normalize.js +30 -0
  107. package/build/src/index.d.ts +2 -0
  108. package/build/src/index.js +1 -0
  109. package/build/src/kanshi/read.js +17 -4
  110. package/build/src/kanshi/report.d.ts +1 -0
  111. package/build/src/library/akuma-creation.d.ts +2 -0
  112. package/build/src/library/akuma-creation.js +33 -17
  113. package/build/src/library/bind.js +18 -16
  114. package/build/src/library/contract-bind.d.ts +3 -1
  115. package/build/src/library/contract-bind.js +5 -3
  116. package/build/src/library/contract-operations.d.ts +2 -2
  117. package/build/src/library/contract-operations.js +1 -1
  118. package/build/src/library/contract-types.d.ts +1 -0
  119. package/build/src/library/fleet.d.ts +4 -1
  120. package/build/src/library/fleet.js +32 -8
  121. package/build/src/plugin/akuma-signals.d.ts +6 -0
  122. package/build/src/plugin/akuma-signals.js +8 -0
  123. package/build/src/plugin/public.d.ts +4 -0
  124. package/build/src/plugin/runtime.js +4 -1
  125. package/build/src/protocol/amend.js +4 -3
  126. package/build/src/protocol/attempt.d.ts +2 -0
  127. package/build/src/protocol/bind.js +41 -14
  128. package/build/src/protocol/completion.d.ts +3 -0
  129. package/build/src/protocol/completion.js +7 -1
  130. package/build/src/protocol/deliver.js +165 -92
  131. package/build/src/protocol/execution-observation.d.ts +0 -4
  132. package/build/src/protocol/intent.d.ts +15 -20
  133. package/build/src/protocol/intent.js +29 -17
  134. package/build/src/protocol/operations.d.ts +2 -0
  135. package/build/src/protocol/placement.js +5 -1
  136. package/build/src/protocol/read/status.d.ts +1 -0
  137. package/build/src/protocol/read/status.js +10 -2
  138. package/build/src/protocol/reintegrate.d.ts +1 -1
  139. package/build/src/protocol/reintegrate.js +70 -43
  140. package/build/src/protocol/result-codec.js +9 -7
  141. package/build/src/protocol/review.js +53 -30
  142. package/build/src/protocol/run.d.ts +83 -36
  143. package/build/src/protocol/run.js +90 -75
  144. package/build/src/runtime/proc/windows-launch.exe +0 -0
  145. package/build/src/settings.js +5 -14
  146. package/build/src/task/document.d.ts +5 -1
  147. package/build/src/task/document.js +6 -1
  148. package/build/src/verification/execution.d.ts +0 -1
  149. package/build/src/verification/execution.js +0 -24
  150. package/build/src/verification/observation.d.ts +0 -4
  151. package/build/src/verification/observation.js +1 -2
  152. package/package.json +3 -1
  153. package/build/src/git/verification-environment.d.ts +0 -10
  154. package/build/src/git/verification-environment.js +0 -281
@@ -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));
@@ -109,10 +109,10 @@ export function decodeDeliverConflictRefusal(value) {
109
109
  const object = record(value, ["kind", "contractId", "reason", "targetHead", "conflictPaths", "recovery"]);
110
110
  if (object.kind !== "integration-failed" || object.reason !== "conflict")
111
111
  fail();
112
- const recovery = record(object.recovery, ["materialize", "continue", "staging"]);
112
+ const recovery = record(object.recovery, ["materialize", "deliver", "staging"]);
113
113
  if (recovery.materialize !== "deliver --materialize-conflict --include-dirty")
114
114
  fail();
115
- if (recovery.continue !== "deliver --include-dirty")
115
+ if (recovery.deliver !== "deliver --include-dirty")
116
116
  fail();
117
117
  if (recovery.staging !== "not-required")
118
118
  fail();
@@ -124,7 +124,7 @@ export function decodeDeliverConflictRefusal(value) {
124
124
  conflictPaths: strings(object.conflictPaths),
125
125
  recovery: {
126
126
  materialize: "deliver --materialize-conflict --include-dirty",
127
- continue: "deliver --include-dirty",
127
+ deliver: "deliver --include-dirty",
128
128
  staging: "not-required",
129
129
  },
130
130
  };
@@ -299,9 +299,11 @@ export function decodeVerificationCleanupFailure(value) {
299
299
  return { phase: "destroy", name: nonblank(object.name), detail: decodeHookFailure(object.detail) };
300
300
  }
301
301
  export function decodeCandidateCompletion(value) {
302
- const object = record(value, ["integration"], ["verification"]);
302
+ const object = record(value, ["integration"], ["predecessor", "target", "verification"]);
303
303
  const completion = {
304
304
  integration: decodeSnapshotId(object.integration),
305
+ ...(object.predecessor === undefined ? {} : { predecessor: decodeSnapshotId(object.predecessor) }),
306
+ ...(object.target === undefined ? {} : { target: nonblank(object.target) }),
305
307
  ...(object.verification === undefined
306
308
  ? {}
307
309
  : {
@@ -339,10 +341,10 @@ export function decodeMaterializedConflict(value) {
339
341
  const object = record(value, ["kind", "targetHead", "conflictPaths", "workspace", "handoffBase", "recovery"]);
340
342
  if (object.kind !== "integration-conflict-materialized")
341
343
  fail();
342
- const recovery = record(object.recovery, ["materialize", "continue", "staging"]);
344
+ const recovery = record(object.recovery, ["materialize", "deliver", "staging"]);
343
345
  if (recovery.materialize !== "deliver --materialize-conflict --include-dirty")
344
346
  fail();
345
- if (recovery.continue !== "deliver --include-dirty")
347
+ if (recovery.deliver !== "deliver --include-dirty")
346
348
  fail();
347
349
  if (recovery.staging !== "not-required")
348
350
  fail();
@@ -354,7 +356,7 @@ export function decodeMaterializedConflict(value) {
354
356
  handoffBase: decodeSnapshotId(object.handoffBase),
355
357
  recovery: {
356
358
  materialize: "deliver --materialize-conflict --include-dirty",
357
- continue: "deliver --include-dirty",
359
+ deliver: "deliver --include-dirty",
358
360
  staging: "not-required",
359
361
  },
360
362
  };
@@ -2,7 +2,7 @@ import { worktreeChangeId } from "../git/integration.js";
2
2
  import { captureTender, dirtyTenderDelta, dirtyTenderRefusal, } from "../git/tender.js";
3
3
  import { observeContractsForAdmissionAt } from "../git/observe.js";
4
4
  import { unmergedWorkspacePaths, worktreePath } from "../git/workspace.js";
5
- import { matchingPrivateRootObservation, privateStateSeatAttempt, sameSpeculativeWorktreeInput, } from "./run.js";
5
+ import { contractStateWitness, privateStateSeatAttempt, sameWorktreeWitness, STALE_PRIVATE_STATE_PREPARATION, } from "./run.js";
6
6
  import { dependencyKeySet } from "../core/subject.js";
7
7
  import { contractState } from "../core/facts/observation.js";
8
8
  import { gate } from "../core/facts/types.js";
@@ -58,14 +58,14 @@ export async function prepareReview(repository, stage) {
58
58
  },
59
59
  };
60
60
  }
61
- function preparedReviewCapture(input, state, prepared) {
61
+ function preparedReviewCapture(input, state, changeId) {
62
62
  return {
63
63
  kind: "prepared",
64
64
  data: {
65
65
  gate: REVIEWED,
66
66
  subject: dependencyKeySet([
67
67
  { kind: "document", value: state.terms.document.key },
68
- { kind: "change", value: prepared.data.changeId },
68
+ { kind: "change", value: changeId },
69
69
  ]),
70
70
  verdict: input.verdict,
71
71
  ...(input.summary === undefined ? {} : { summary: input.summary }),
@@ -88,32 +88,56 @@ async function recaptureReviewWorktree(input, state) {
88
88
  });
89
89
  return prepared.kind === "prepared" ? reviewWorktreeInput(prepared) : undefined;
90
90
  }
91
- async function speculateReview(input) {
91
+ /** Capture the reviewable worktree from one non-authoritative private-state read. */
92
+ async function prepareExternalReview(input) {
92
93
  const observation = await observeContractsForAdmissionAt(input.scope, input.channel, [input.contractId]);
93
94
  const state = contractState(observation.decision, input.contractId);
94
95
  if (state === null)
95
- return { observation };
96
+ return { kind: "unavailable" };
96
97
  const prepared = await captureReviewableWorktree(input.scope, {
97
98
  contractId: state.id,
98
99
  coordinates: state.coordinates,
99
100
  });
100
- if (prepared.kind === "refused") {
101
- return { observation, preparation: { kind: "refused", refusal: prepared.refusal } };
101
+ return {
102
+ kind: "artifacts",
103
+ witness: contractStateWitness(state),
104
+ artifacts: prepared.kind === "refused"
105
+ ? { kind: "refused", refusal: prepared.refusal }
106
+ : {
107
+ kind: "captured",
108
+ ...(prepared.data.workspace === undefined ? {} : { workspace: prepared.data.workspace }),
109
+ worktree: reviewWorktreeInput(prepared),
110
+ },
111
+ };
112
+ }
113
+ /** Assemble the in-custody attestation input from the fresh Contract and validated artifacts. */
114
+ async function assembleReviewAttempt(input, observation, external) {
115
+ const state = contractState(observation.decision, input.contractId);
116
+ if (external.kind === "unavailable")
117
+ return state === null ? { kind: "assembled" } : { kind: "stale" };
118
+ if (state === null)
119
+ return { kind: "stale" };
120
+ if (external.witness.head !== state.head)
121
+ return { kind: "stale" };
122
+ if (external.artifacts.kind === "refused") {
123
+ return { kind: "assembled", preparation: { kind: "refused", refusal: external.artifacts.refusal } };
124
+ }
125
+ if (!sameWorktreeWitness(external.artifacts.worktree, await recaptureReviewWorktree(input, state))) {
126
+ return { kind: "stale" };
102
127
  }
103
128
  return {
104
- observation,
105
- preparation: preparedReviewCapture(input, state, prepared),
106
- ...(prepared.data.workspace === undefined ? {} : { workspace: prepared.data.workspace }),
107
- worktree: reviewWorktreeInput(prepared),
129
+ kind: "assembled",
130
+ preparation: preparedReviewCapture(input, state, external.artifacts.worktree.changeId),
131
+ ...(external.artifacts.workspace === undefined ? {} : { workspace: external.artifacts.workspace }),
108
132
  };
109
133
  }
110
- async function decideAndAdmitReview(input, attempt, seat, observation, speculated) {
134
+ async function decideAndAdmitReview(input, attempt, seat, observation, assembled) {
111
135
  const decision = decideAttestation({
112
136
  input: {
113
137
  contractId: input.contractId,
114
138
  ...(input.actor === undefined ? {} : { actor: input.actor }),
115
139
  at: timestamp(),
116
- ...(speculated.preparation === undefined ? {} : { preparation: speculated.preparation }),
140
+ ...(assembled.preparation === undefined ? {} : { preparation: assembled.preparation }),
117
141
  },
118
142
  attempt,
119
143
  observation: observation.decision,
@@ -128,33 +152,30 @@ async function decideAndAdmitReview(input, attempt, seat, observation, speculate
128
152
  attempt,
129
153
  offer: decision.offer,
130
154
  primaryContract: input.contractId,
131
- ...(input.progress === undefined ? {} : input.progress === undefined ? {} : { progress: input.progress }),
155
+ ...(input.progress === undefined ? {} : { progress: input.progress }),
132
156
  });
133
157
  if (admission.kind !== "accepted")
134
158
  return admission;
135
159
  return {
136
160
  ...admission,
137
161
  value: {
138
- ...(speculated.workspace === undefined ? {} : { workspace: speculated.workspace }),
162
+ ...(assembled.workspace === undefined ? {} : { workspace: assembled.workspace }),
139
163
  },
140
164
  };
141
165
  }
142
- async function reviewAttemptInPrivateStateSeat(input, attempt, seat, speculated) {
143
- const observation = await matchingPrivateRootObservation(input.scope, input.channel, input.contractId, speculated.observation, async (fresh) => {
144
- if (speculated.worktree === undefined)
145
- return true;
146
- const state = contractState(fresh.decision, input.contractId);
147
- if (state === null)
148
- return false;
149
- return sameSpeculativeWorktreeInput(speculated.worktree, await recaptureReviewWorktree(input, state));
150
- });
151
- return "kind" in observation
152
- ? observation
153
- : await decideAndAdmitReview(input, attempt, seat, observation, speculated);
154
- }
166
+ /**
167
+ * One review attempt: the repeatable worktree capture happens outside custody, then a single
168
+ * in-custody observation, witness validation, subject assembly, decision, and publication.
169
+ */
155
170
  async function reviewAttempt(input, attempt) {
156
- const speculated = await speculateReview(input);
157
- return await privateStateSeatAttempt(input.scope, async (seat) => await reviewAttemptInPrivateStateSeat(input, attempt, seat, speculated), attemptDecisionWithSeatClose);
171
+ const external = await prepareExternalReview(input);
172
+ return await privateStateSeatAttempt(input.scope, async (seat) => {
173
+ const observation = await observeContractsForAdmissionAt(input.scope, input.channel, [input.contractId]);
174
+ const assembled = await assembleReviewAttempt(input, observation, external);
175
+ if (assembled.kind === "stale")
176
+ return STALE_PRIVATE_STATE_PREPARATION;
177
+ return await decideAndAdmitReview(input, attempt, seat, observation, assembled);
178
+ }, attemptDecisionWithSeatClose);
158
179
  }
159
180
  export async function admitReviewOperation(input) {
160
181
  const attempts = mintAttempts({ entryCount: 1 });
@@ -167,6 +188,8 @@ export async function admitReviewOperation(input) {
167
188
  }
168
189
  if (result.kind === "publication-failed")
169
190
  return { kind: "retry", reason: result };
191
+ if (result.kind === "stale" || result.kind === "redecide")
192
+ continue;
170
193
  if (result.kind === "collision" && index + 1 === attempts.length)
171
194
  return { kind: "retry", reason: result };
172
195
  }
@@ -3,15 +3,14 @@ import { type GitDecisionObservation } from "../git/observe.js";
3
3
  import { type PrivateStatePublicationSeat, type PrivateStateSeatOutcome } from "../git/private-state-seat.js";
4
4
  import type { GitDecodeChannel, GitTreeSelection } from "../git/read-observation.js";
5
5
  import type { GitRepository } from "../git/process.js";
6
- import { type GitOid, type GitRefAssertion } from "../git/repository.js";
6
+ import { type GitRefAssertion } from "../git/repository.js";
7
7
  import type { AttemptContext, DecideInput, OfferDecision } from "../core/decide.js";
8
8
  import type { Offer, TreeUpdate } from "../core/facts/offer.js";
9
- import { type ChangeId, type ContractId, type SnapshotId } from "../core/facts/types.js";
10
- import { type AcceptedAdmission, type AttemptTerminal } from "./attempt.js";
9
+ import type { ChangeId, ContractHead, ContractId, SnapshotId } from "../core/facts/types.js";
11
10
  import type { GitObjectId } from "../git/identity.js";
11
+ import { type AcceptedAdmission, type AttemptTerminal } from "./attempt.js";
12
12
  export declare const STALE_PRIVATE_STATE_PREPARATION: {
13
- readonly kind: "publication-failed";
14
- readonly diagnostic: "stale private-state preparation";
13
+ readonly kind: "stale";
15
14
  };
16
15
  export type ProtocolTerminal = Readonly<{
17
16
  kind: "exhausted";
@@ -26,11 +25,40 @@ export type CompanionDecorator = (input: Readonly<{
26
25
  contractId: ContractId;
27
26
  offer: Offer;
28
27
  }>) => readonly TreeUpdate[] | Promise<readonly TreeUpdate[]>;
29
- export type RunProtocolInput<Input extends Readonly<{
30
- contractId: ContractId;
31
- }>, Refusal, Seed extends Readonly<{
28
+ /**
29
+ * Seat-external preparation: immutable artifacts plus the currentness witnesses they carry.
30
+ * It never observes private state as authority and never forms an offer.
31
+ */
32
+ export type ExternalProtocolPreparation<Prepared, Refusal> = Readonly<{
33
+ kind: "prepared";
34
+ prepared: Prepared;
35
+ assertions?: readonly GitRefAssertion[];
36
+ }> | Readonly<{
37
+ kind: "refused";
38
+ refusal: Refusal;
39
+ }>;
40
+ /**
41
+ * In-custody assembly of one decision input from the fresh observation and an external artifact.
42
+ * `stale` means only that the outside preparation is spent: discard it and restart the cycle.
43
+ */
44
+ export type InCustodyProtocolPreparation<Input, Refusal> = Readonly<{
45
+ kind: "prepared";
46
+ input: Input;
47
+ assertions?: readonly GitRefAssertion[];
48
+ }> | Readonly<{
49
+ kind: "stale";
50
+ }> | Readonly<{
51
+ kind: "refused";
52
+ refusal: Refusal;
53
+ }>;
54
+ /** The one split-preparation mechanism: repeatable work outside custody, assembly inside it. */
55
+ export type ProtocolPreparation<Input, Refusal, Seed, Prepared> = Readonly<{
56
+ external: (seed: Seed) => ExternalProtocolPreparation<Prepared, Refusal> | Promise<ExternalProtocolPreparation<Prepared, Refusal>>;
57
+ assemble: (observation: GitDecisionObservation, seed: Seed, prepared: Prepared) => InCustodyProtocolPreparation<Input, Refusal> | Promise<InCustodyProtocolPreparation<Input, Refusal>>;
58
+ }>;
59
+ type ProtocolAdmission<Input extends Readonly<{
32
60
  contractId: ContractId;
33
- }> = Input> = Readonly<{
61
+ }>, Refusal> = Readonly<{
34
62
  channel: GitDecodeChannel;
35
63
  repository: GitRepository;
36
64
  progress?: ExecutionProgress;
@@ -45,28 +73,31 @@ export type RunProtocolInput<Input extends Readonly<{
45
73
  decorateOffer?: CompanionDecorator;
46
74
  validateAdmission?: (observation: GitDecisionObservation) => Refusal | undefined | Promise<Refusal | undefined>;
47
75
  observationSelection?: GitTreeSelection;
48
- }> & (Readonly<{
49
- input: Input;
50
- prepareInput?: never;
51
- }> | Readonly<{
76
+ }>;
77
+ /** An intent whose invocation is already its decision input. */
78
+ export type DirectRunProtocolInput<Seed extends Readonly<{
79
+ contractId: ContractId;
80
+ }>, Refusal> = ProtocolAdmission<Seed, Refusal> & Readonly<{
81
+ input: Seed;
82
+ preparation?: undefined;
83
+ }>;
84
+ /** An intent whose repeatable preparation runs outside custody and is assembled inside it. */
85
+ export type SplitRunProtocolInput<Input extends Readonly<{
86
+ contractId: ContractId;
87
+ }>, Refusal, Seed extends Readonly<{
88
+ contractId: ContractId;
89
+ }>, Prepared> = ProtocolAdmission<Input, Refusal> & Readonly<{
52
90
  input: Seed;
53
- prepareInput: (observation: GitDecisionObservation, input: Seed) => Readonly<{
54
- kind: "prepared";
55
- input: Input;
56
- assertions?: readonly GitRefAssertion[];
57
- }> | Readonly<{
58
- kind: "refused";
59
- refusal: Refusal;
60
- }> | Promise<Readonly<{
61
- kind: "prepared";
62
- input: Input;
63
- assertions?: readonly GitRefAssertion[];
64
- }> | Readonly<{
65
- kind: "refused";
66
- refusal: Refusal;
67
- }>>;
68
- }>);
91
+ preparation: ProtocolPreparation<Input, Refusal, Seed, Prepared>;
92
+ }>;
93
+ export type RunProtocolInput<Input extends Readonly<{
94
+ contractId: ContractId;
95
+ }>, Refusal, Seed extends Readonly<{
96
+ contractId: ContractId;
97
+ }> = Input, Prepared = Seed> = DirectRunProtocolInput<Seed, Refusal> | SplitRunProtocolInput<Input, Refusal, Seed, Prepared>;
69
98
  export type PreparedProtocolAttempt<Refusal> = Readonly<{
99
+ kind: "stale";
100
+ }> | Readonly<{
70
101
  kind: "refused";
71
102
  refusal: Refusal;
72
103
  }> | Readonly<{
@@ -76,29 +107,45 @@ export type PreparedProtocolAttempt<Refusal> = Readonly<{
76
107
  offer: Offer;
77
108
  assertions: readonly GitRefAssertion[];
78
109
  }>;
79
- export declare function privateRootCommit(observation: GitDecisionObservation): GitOid | null;
80
- export declare function samePrivateRootObservation(prepared: GitDecisionObservation, fresh: GitDecisionObservation): boolean;
81
- export type SpeculativeWorktreeInput = Readonly<{
110
+ /**
111
+ * The private-state witness a seat-external preparation was computed against. Equal journal heads
112
+ * mean the same decoded contract state, so the same document, coordinates, and facts.
113
+ */
114
+ export type ContractStateWitness = Readonly<{
115
+ head: ContractHead | null;
116
+ }>;
117
+ export declare function contractStateWitness(state: ContractStateWitness): ContractStateWitness;
118
+ /**
119
+ * A physical worktree capture carried across custody as a witness, never as authority.
120
+ *
121
+ * Unlike a ref or a journal head, uncommitted worktree bytes are covered by no atomic
122
+ * compare-and-swap, so the only sound proof that a seat-external capture still describes the
123
+ * bytes about to be published is a fresh capture compared against this witness under custody.
124
+ * A cheaper structural signature (head, merge head, dirty flag, changed paths) cannot judge it:
125
+ * a writer that rewrites an already-changed path - a conflict materialization, for instance -
126
+ * leaves every path-level signature identical while the bytes that would be published differ.
127
+ */
128
+ export type WorktreeWitness = Readonly<{
82
129
  tree: GitObjectId;
83
130
  head: SnapshotId;
84
131
  mergeHead?: SnapshotId;
85
132
  dirty: boolean;
86
133
  changeId: ChangeId;
87
134
  }>;
88
- export declare function sameSpeculativeWorktreeInput(prepared: SpeculativeWorktreeInput | undefined, fresh: SpeculativeWorktreeInput | undefined): boolean;
135
+ export declare function sameWorktreeWitness(prepared: WorktreeWitness | undefined, fresh: WorktreeWitness | undefined): boolean;
89
136
  export declare function privateStateAssertionsMatch(repository: GitRepository, assertions: readonly GitRefAssertion[]): Promise<boolean>;
90
137
  export declare function publicationFailedFromSeatError(error: unknown): AttemptTerminal;
91
- export declare function matchingPrivateRootObservation(repository: GitRepository, channel: GitDecodeChannel, contractId: ContractId, prepared: GitDecisionObservation, revalidate?: (fresh: GitDecisionObservation) => boolean | Promise<boolean>): Promise<GitDecisionObservation | typeof STALE_PRIVATE_STATE_PREPARATION>;
92
138
  export declare function privateStateSeatAttempt<T>(repository: GitRepository, action: (seat: PrivateStatePublicationSeat) => Promise<T>, wrap: (outcome: PrivateStateSeatOutcome<T>) => T): Promise<T | AttemptTerminal>;
93
139
  /** Form one decided offer without admitting or reinterpreting it. */
94
140
  export declare function prepareProtocolAttempt<Input extends Readonly<{
95
141
  contractId: ContractId;
96
142
  }>, Refusal, Seed extends Readonly<{
97
143
  contractId: ContractId;
98
- }> = Input>(input: RunProtocolInput<Input, Refusal, Seed>, baseAttempt: AttemptContext): Promise<PreparedProtocolAttempt<Refusal>>;
144
+ }> = Input, Prepared = Seed>(input: RunProtocolInput<Input, Refusal, Seed, Prepared>, baseAttempt: AttemptContext): Promise<PreparedProtocolAttempt<Refusal>>;
99
145
  /** Run bounded, verb-neutral attempts and return one real admission on acceptance. */
100
146
  export declare function runProtocol<Input extends Readonly<{
101
147
  contractId: ContractId;
102
148
  }>, Refusal, Seed extends Readonly<{
103
149
  contractId: ContractId;
104
- }> = Input>(input: RunProtocolInput<Input, Refusal, Seed>): Promise<ProtocolResult<Refusal>>;
150
+ }> = Input, Prepared = Seed>(input: RunProtocolInput<Input, Refusal, Seed, Prepared>): Promise<ProtocolResult<Refusal>>;
151
+ export {};