@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.
- package/README.md +4 -4
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +14 -7
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +53 -20
- 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 -4
- 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 +23 -0
- package/build/src/akuma/akuma-product.d.ts +2 -0
- package/build/src/akuma/akuma-product.js +6 -0
- package/build/src/akuma/akuma.d.ts +20 -1
- package/build/src/akuma/akuma.js +2 -0
- package/build/src/akuma/allowed.d.ts +20 -0
- package/build/src/akuma/allowed.js +11 -0
- package/build/src/akuma/archetype.js +3 -0
- package/build/src/akuma/body.d.ts +25 -5
- package/build/src/akuma/body.js +70 -31
- package/build/src/akuma/call-request.d.ts +5 -0
- package/build/src/akuma/call-request.js +14 -4
- package/build/src/akuma/fleet-execution.d.ts +28 -0
- package/build/src/akuma/fleet-execution.js +36 -4
- package/build/src/akuma/fleet-observation.d.ts +54 -0
- package/build/src/akuma/fleet-request.d.ts +10 -0
- package/build/src/akuma/fleet-request.js +13 -3
- 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-recipe.d.ts +1 -0
- package/build/src/akuma/provider-recipe.js +5 -0
- 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/acp/index.js +3 -0
- package/build/src/akuma/providers/claude/index.js +10 -10
- package/build/src/akuma/providers/codex-app-server/index.js +12 -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/grok-build/index.js +3 -0
- package/build/src/akuma/providers/opencode-sdk/index.js +32 -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 +5 -2
- package/build/src/akuma/request-observation.d.ts +45 -0
- package/build/src/akuma/request-observation.js +162 -0
- package/build/src/akuma/request-rendezvous.d.ts +6 -3
- package/build/src/akuma/request-rendezvous.js +80 -14
- package/build/src/akuma/request-serve.js +27 -4
- package/build/src/akuma/request-wire.d.ts +5 -0
- package/build/src/akuma/request-wire.js +2 -0
- 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 +5 -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 +3 -1
- package/build/src/cli/commands/akuma-invoke.js +134 -14
- package/build/src/cli/commands/akuma.js +24 -12
- package/build/src/cli/commands/contract-help.d.ts +15 -15
- package/build/src/cli/commands/contract-help.js +45 -19
- package/build/src/cli/commands/contract-invoke.d.ts +3 -0
- package/build/src/cli/commands/contract-invoke.js +25 -11
- package/build/src/cli/commands/contract.js +6 -4
- package/build/src/cli/commands/install.d.ts +1 -1
- package/build/src/cli/commands/install.js +1 -1
- package/build/src/cli/commands/status-set.js +15 -24
- package/build/src/cli/commands/task.js +18 -18
- package/build/src/cli/invoke.d.ts +3 -0
- package/build/src/cli/invoke.js +58 -23
- package/build/src/cli/main.js +5 -0
- package/build/src/cli/parse.d.ts +3 -0
- package/build/src/cli/parse.js +26 -4
- package/build/src/cli/render/akuma-activity.d.ts +100 -2
- package/build/src/cli/render/akuma-activity.js +485 -40
- package/build/src/cli/render/akuma.d.ts +10 -0
- package/build/src/cli/render/akuma.js +31 -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 +26 -0
- package/build/src/cli/render/execution-progress.js +178 -0
- 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 +6 -6
- 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 +36 -4
- 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/hooks.d.ts +13 -1
- package/build/src/git/hooks.js +2 -1
- 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 +34 -18
- package/build/src/library/audit.d.ts +2 -0
- package/build/src/library/audit.js +1 -0
- package/build/src/library/bind.js +18 -16
- package/build/src/library/composition.d.ts +1 -0
- package/build/src/library/contract-bind.d.ts +3 -1
- package/build/src/library/contract-bind.js +5 -3
- package/build/src/library/contract-execution.d.ts +2 -0
- package/build/src/library/contract-execution.js +15 -1
- package/build/src/library/contract-forwarding.d.ts +4 -0
- package/build/src/library/contract-forwarding.js +3 -0
- package/build/src/library/contract-handle.d.ts +7 -0
- package/build/src/library/contract-handle.js +25 -0
- package/build/src/library/contract-operations.d.ts +7 -2
- package/build/src/library/contract-operations.js +21 -1
- package/build/src/library/contract-types.d.ts +1 -0
- package/build/src/library/execution-result.d.ts +2 -2
- package/build/src/library/execution.d.ts +8 -0
- package/build/src/library/execution.js +102 -0
- package/build/src/library/fleet.d.ts +4 -1
- package/build/src/library/fleet.js +32 -8
- package/build/src/library/keiyaku.d.ts +2 -0
- package/build/src/library/mutation.js +15 -7
- package/build/src/plugin/akuma-signals.d.ts +6 -0
- package/build/src/plugin/akuma-signals.js +9 -1
- package/build/src/plugin/public.d.ts +6 -2
- package/build/src/plugin/runtime.d.ts +1 -3
- package/build/src/plugin/runtime.js +95 -103
- 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 +35 -11
- package/build/src/protocol/deliver.js +165 -92
- package/build/src/protocol/execution-observation.d.ts +181 -0
- package/build/src/protocol/execution-observation.js +45 -0
- package/build/src/protocol/intent.d.ts +18 -22
- package/build/src/protocol/intent.js +63 -41
- package/build/src/protocol/operations.d.ts +2 -0
- package/build/src/protocol/placement.js +5 -1
- package/build/src/protocol/progress.d.ts +5 -1
- package/build/src/protocol/progress.js +10 -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 +27 -11
- 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/run.d.ts +16 -4
- package/build/src/runtime/proc/run.js +64 -22
- 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 +10 -2
- package/build/src/verification/execution.js +143 -42
- package/build/src/verification/observation.d.ts +41 -0
- package/build/src/verification/observation.js +29 -0
- 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
|
-
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
83
|
-
|
|
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
|
-
...(
|
|
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
|
|
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
|
|
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
|
|
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
|
}
|
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
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));
|
|
@@ -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", "
|
|
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.
|
|
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
|
-
|
|
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", "
|
|
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.
|
|
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
|
-
|
|
359
|
+
deliver: "deliver --include-dirty",
|
|
344
360
|
staging: "not-required",
|
|
345
361
|
},
|
|
346
362
|
};
|