@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.
- package/README.md +4 -4
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +3 -3
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +22 -8
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/references/automation.md +237 -0
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +6 -7
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +2 -3
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +3 -3
- package/build/src/akuma/akuma-handle.d.ts +5 -1
- package/build/src/akuma/akuma-handle.js +4 -1
- package/build/src/akuma/akuma-instance.d.ts +7 -2
- package/build/src/akuma/akuma-instance.js +14 -7
- package/build/src/akuma/akuma-observe.d.ts +11 -0
- package/build/src/akuma/akuma-observe.js +22 -0
- package/build/src/akuma/akuma-product.d.ts +1 -0
- package/build/src/akuma/akuma-product.js +3 -0
- package/build/src/akuma/akuma.d.ts +1 -0
- package/build/src/akuma/body.d.ts +25 -5
- package/build/src/akuma/body.js +65 -8
- package/build/src/akuma/call-request.d.ts +2 -0
- package/build/src/akuma/call-request.js +2 -0
- package/build/src/akuma/fleet-execution.d.ts +28 -0
- package/build/src/akuma/fleet-execution.js +36 -4
- package/build/src/akuma/fleet-request.d.ts +10 -0
- package/build/src/akuma/fleet-request.js +11 -2
- package/build/src/akuma/heart/facts.d.ts +2 -0
- package/build/src/akuma/heart/index.d.ts +1 -0
- package/build/src/akuma/heart/index.js +4 -1
- package/build/src/akuma/heart/rows.d.ts +2 -0
- package/build/src/akuma/heart/rows.js +7 -5
- package/build/src/akuma/heart/schema.d.ts +1 -1
- package/build/src/akuma/heart/schema.js +3 -1
- package/build/src/akuma/heart/tells.js +6 -5
- package/build/src/akuma/heart/timeline.js +1 -1
- package/build/src/akuma/provider.d.ts +1 -1
- package/build/src/akuma/provider.js +1 -1
- package/build/src/akuma/providers/acp/core.js +3 -6
- package/build/src/akuma/providers/claude/index.js +9 -12
- package/build/src/akuma/providers/codex-app-server/index.js +4 -11
- package/build/src/akuma/providers/execution-environment.d.ts +6 -0
- package/build/src/akuma/providers/execution-environment.js +24 -0
- package/build/src/akuma/providers/opencode-sdk/index.js +30 -23
- package/build/src/akuma/providers/opencode-sdk/session.d.ts +8 -1
- package/build/src/akuma/providers/opencode-sdk/session.js +6 -5
- package/build/src/akuma/providers/pi/events.js +7 -2
- package/build/src/akuma/providers/pi/index.js +3 -2
- package/build/src/akuma/request-rendezvous.js +8 -3
- package/build/src/akuma/turn-drive.d.ts +1 -0
- package/build/src/akuma/turn-drive.js +2 -0
- package/build/src/akuma-body.js +2 -0
- package/build/src/body/amend.js +6 -4
- package/build/src/body/decode.d.ts +3 -1
- package/build/src/body/decode.js +53 -17
- package/build/src/body/region.d.ts +9 -1
- package/build/src/body/region.js +87 -12
- package/build/src/body/render.js +1 -1
- package/build/src/body/verification.d.ts +3 -1
- package/build/src/body/verification.js +4 -1
- package/build/src/cli/accepted.js +19 -2
- package/build/src/cli/commands/akuma-invoke.d.ts +2 -0
- package/build/src/cli/commands/akuma-invoke.js +133 -13
- package/build/src/cli/commands/akuma.js +11 -4
- package/build/src/cli/commands/contract-help.js +17 -4
- package/build/src/cli/commands/contract.js +6 -4
- package/build/src/cli/commands/status-set.js +15 -24
- package/build/src/cli/main.js +5 -0
- package/build/src/cli/parse.d.ts +2 -0
- package/build/src/cli/parse.js +5 -0
- package/build/src/cli/render/akuma-activity.d.ts +99 -2
- package/build/src/cli/render/akuma-activity.js +484 -40
- package/build/src/cli/render/akuma.d.ts +10 -0
- package/build/src/cli/render/akuma.js +28 -21
- package/build/src/cli/render/audit.js +1 -1
- package/build/src/cli/render/board.js +1 -1
- package/build/src/cli/render/catalog.js +16 -13
- package/build/src/cli/render/contract-history.js +39 -10
- package/build/src/cli/render/contract-observation.js +10 -6
- package/build/src/cli/render/contract.js +109 -106
- package/build/src/cli/render/execution-progress.d.ts +23 -1
- package/build/src/cli/render/execution-progress.js +151 -28
- package/build/src/cli/render/kanshi-akuma.js +27 -39
- package/build/src/cli/render/kanshi.js +33 -26
- package/build/src/cli/render/nuke.js +1 -1
- package/build/src/cli/render/receipt.d.ts +1 -1
- package/build/src/cli/render/receipt.js +5 -5
- package/build/src/cli/render/refusal.js +10 -7
- package/build/src/cli/render/region.js +1 -1
- package/build/src/cli/render/settings.js +12 -2
- package/build/src/cli/render/status-line.d.ts +30 -0
- package/build/src/cli/render/status-line.js +58 -0
- package/build/src/cli/render/status-set.js +1 -1
- package/build/src/cli/render/task.d.ts +2 -0
- package/build/src/cli/render/task.js +11 -7
- package/build/src/cli/render/terminal.d.ts +0 -1
- package/build/src/cli/render/terminal.js +47 -15
- package/build/src/cli/result.d.ts +1 -0
- package/build/src/cli/runtime.d.ts +2 -0
- package/build/src/cli/runtime.js +13 -7
- package/build/src/cli/usage.js +2 -2
- package/build/src/cli/version.d.ts +1 -0
- package/build/src/cli/version.js +25 -0
- package/build/src/git/workspace.d.ts +1 -1
- package/build/src/git/workspace.js +1 -1
- package/build/src/identity/mint.d.ts +13 -0
- package/build/src/identity/mint.js +26 -0
- package/build/src/identity/normalize.d.ts +4 -0
- package/build/src/identity/normalize.js +30 -0
- package/build/src/index.d.ts +2 -0
- package/build/src/index.js +1 -0
- package/build/src/kanshi/read.js +17 -4
- package/build/src/kanshi/report.d.ts +1 -0
- package/build/src/library/akuma-creation.d.ts +2 -0
- package/build/src/library/akuma-creation.js +33 -17
- package/build/src/library/bind.js +18 -16
- package/build/src/library/contract-bind.d.ts +3 -1
- package/build/src/library/contract-bind.js +5 -3
- package/build/src/library/contract-operations.d.ts +2 -2
- package/build/src/library/contract-operations.js +1 -1
- package/build/src/library/contract-types.d.ts +1 -0
- package/build/src/library/fleet.d.ts +4 -1
- package/build/src/library/fleet.js +32 -8
- package/build/src/plugin/akuma-signals.d.ts +6 -0
- package/build/src/plugin/akuma-signals.js +8 -0
- package/build/src/plugin/public.d.ts +4 -0
- package/build/src/plugin/runtime.js +4 -1
- package/build/src/protocol/amend.js +4 -3
- package/build/src/protocol/attempt.d.ts +2 -0
- package/build/src/protocol/bind.js +41 -14
- package/build/src/protocol/completion.d.ts +3 -0
- package/build/src/protocol/completion.js +7 -1
- package/build/src/protocol/deliver.js +165 -92
- package/build/src/protocol/execution-observation.d.ts +0 -4
- package/build/src/protocol/intent.d.ts +15 -20
- package/build/src/protocol/intent.js +29 -17
- package/build/src/protocol/operations.d.ts +2 -0
- package/build/src/protocol/placement.js +5 -1
- package/build/src/protocol/read/status.d.ts +1 -0
- package/build/src/protocol/read/status.js +10 -2
- package/build/src/protocol/reintegrate.d.ts +1 -1
- package/build/src/protocol/reintegrate.js +70 -43
- package/build/src/protocol/result-codec.js +9 -7
- package/build/src/protocol/review.js +53 -30
- package/build/src/protocol/run.d.ts +83 -36
- package/build/src/protocol/run.js +90 -75
- package/build/src/runtime/proc/windows-launch.exe +0 -0
- package/build/src/settings.js +5 -14
- package/build/src/task/document.d.ts +5 -1
- package/build/src/task/document.js +6 -1
- package/build/src/verification/execution.d.ts +0 -1
- package/build/src/verification/execution.js +0 -24
- package/build/src/verification/observation.d.ts +0 -4
- package/build/src/verification/observation.js +1 -2
- package/package.json +3 -1
- package/build/src/git/verification-environment.d.ts +0 -10
- 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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
|
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 ? {} :
|
|
69
|
-
assertions,
|
|
90
|
+
...(input.progress === undefined ? {} : { progress: input.progress }),
|
|
91
|
+
...(assembled.kind === "prepared" ? { assertions: [assembled.assertion] } : {}),
|
|
70
92
|
});
|
|
71
|
-
if (
|
|
72
|
-
if (
|
|
93
|
+
if (admitted.kind === "accepted") {
|
|
94
|
+
if (assembled.kind !== "prepared")
|
|
73
95
|
throw new Error("accepted reintegration has no preparation");
|
|
74
|
-
return { ...
|
|
96
|
+
return { ...admitted, value: assembled.data };
|
|
75
97
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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", "
|
|
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.
|
|
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
|
-
|
|
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", "
|
|
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.
|
|
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
|
-
|
|
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 {
|
|
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,
|
|
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:
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
101
|
-
|
|
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
|
-
|
|
105
|
-
preparation: preparedReviewCapture(input, state,
|
|
106
|
-
...(
|
|
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,
|
|
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
|
-
...(
|
|
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 ? {} :
|
|
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
|
-
...(
|
|
162
|
+
...(assembled.workspace === undefined ? {} : { workspace: assembled.workspace }),
|
|
139
163
|
},
|
|
140
164
|
};
|
|
141
165
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
|
157
|
-
return await privateStateSeatAttempt(input.scope, async (seat) =>
|
|
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
|
|
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 {
|
|
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: "
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
}
|
|
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
|
-
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
|
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 {};
|