@astrosheep/keiyaku 2.9.12 → 2.9.13
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 +22 -18
- package/build/.tsbuildinfo +1 -1
- package/build/agents/call-terms.js +1 -0
- package/build/agents/providers/claude-agent-sdk/adapter.js +2 -2
- package/build/cli/commands/akuma/akuma/handler.js +2 -1
- package/build/cli/commands/akuma/akuma/meta.js +4 -3
- package/build/cli/commands/akuma/catalog.js +2 -0
- package/build/cli/commands/akuma/list/handler.js +1 -1
- package/build/cli/commands/akuma/list/meta-list.js +12 -0
- package/build/cli/commands/akuma/list/meta-ls.js +2 -2
- package/build/cli/commands/akuma.js +5 -0
- package/build/cli/commands/contract/amend/meta.js +4 -3
- package/build/cli/commands/contract/arc/meta.js +2 -0
- package/build/cli/commands/contract/audit/handler.js +28 -2
- package/build/cli/commands/contract/audit/meta.js +4 -3
- package/build/cli/commands/contract/petition/handler.js +2 -1
- package/build/cli/commands/contract/petition/meta.js +2 -2
- package/build/cli/commands/contract/renew/handler.js +44 -3
- package/build/cli/commands/contract/renew/meta.js +5 -4
- package/build/cli/commands/metadata.js +12 -9
- package/build/cli/commands/projection/call/handler.js +1 -1
- package/build/cli/commands/projection/call/meta.js +1 -1
- package/build/cli/commands/projection/catalog.js +2 -2
- package/build/cli/commands/projection/{revive → fork}/handler.js +10 -5
- package/build/cli/commands/projection/fork/meta.js +12 -0
- package/build/cli/commands/projection/history/handler.js +46 -2
- package/build/cli/commands/projection/history/meta.js +3 -3
- package/build/cli/commands/projection/kill/meta.js +2 -2
- package/build/cli/commands/projection/status/handler.js +29 -13
- package/build/cli/commands/projection/status/meta.js +14 -3
- package/build/cli/commands/projection/tell/handler.js +8 -4
- package/build/cli/commands/projection/tell/meta.js +1 -1
- package/build/cli/commands/projection/wait/handler.js +45 -20
- package/build/cli/commands/projection/wait/meta.js +1 -1
- package/build/cli/commands/shared.js +1 -1
- package/build/cli/commands/system/catalog.js +2 -0
- package/build/cli/commands/task/add/handler.js +28 -12
- package/build/cli/commands/task/add/meta.js +18 -6
- package/build/cli/commands/task/catalog.js +2 -0
- package/build/cli/commands/task/compose/handler.js +2 -2
- package/build/cli/commands/task/compose/meta.js +2 -2
- package/build/cli/commands/task/doctor/handler.js +2 -3
- package/build/cli/commands/task/doctor/meta.js +2 -2
- package/build/cli/commands/task/done/meta.js +2 -2
- package/build/cli/commands/task/drop/handler.js +7 -2
- package/build/cli/commands/task/drop/meta.js +6 -3
- package/build/cli/commands/task/hold/meta.js +2 -2
- package/build/cli/commands/task/log/meta.js +2 -2
- package/build/cli/commands/task/ls/meta.js +2 -2
- package/build/cli/commands/task/note/handler.js +6 -0
- package/build/cli/commands/task/note/meta.js +11 -0
- package/build/cli/commands/task/resume/meta.js +2 -2
- package/build/cli/commands/task/shared.js +29 -15
- package/build/cli/commands/task/show/meta.js +2 -2
- package/build/cli/commands/task/start/meta.js +2 -2
- package/build/cli/commands/task/stop/meta.js +2 -2
- package/build/cli/commands/task/update/meta.js +3 -2
- package/build/cli/commands/verification/handler.js +43 -0
- package/build/cli/commands/verification/meta.js +13 -0
- package/build/cli/completion.js +16 -13
- package/build/cli/flags.js +43 -11
- package/build/cli/help.js +29 -30
- package/build/cli/index.js +6 -5
- package/build/cli/parse-flags.js +39 -0
- package/build/cli/parse-metadata.js +5 -2
- package/build/cli/parse-selectors.js +18 -0
- package/build/cli/parse.js +21 -8
- package/build/cli/render/arc.js +12 -3
- package/build/cli/render/audit.js +2 -2
- package/build/cli/render/call.js +4 -4
- package/build/cli/render/kanshi.js +122 -85
- package/build/cli/render/misc.js +2 -2
- package/build/cli/render/petition.js +16 -1
- package/build/cli/render/projection-history.js +17 -4
- package/build/cli/render/shared.js +5 -5
- package/build/cli/render/status-indicator.js +47 -0
- package/build/cli/render/status.js +106 -76
- package/build/cli/render/success-response.js +20 -5
- package/build/cli/render/task.js +6 -5
- package/build/cli/render/tell.js +10 -4
- package/build/cli/render/tool-command-normalization.js +137 -0
- package/build/cli/render/tool-presentation.js +2 -1
- package/build/cli/render/verification.js +23 -0
- package/build/cli/render/wait.js +101 -33
- package/build/cli/types.js +2 -1
- package/build/config/akuma-loader.js +21 -1
- package/build/config/settings/knobs.js +7 -1
- package/build/config/settings/schema.js +10 -5
- package/build/core/amend.js +102 -17
- package/build/core/arc.js +4 -4
- package/build/core/audit/candidate.js +10 -18
- package/build/core/audit/coordinates.js +12 -17
- package/build/core/audit/evidence.js +6 -4
- package/build/core/audit/facade.js +3 -3
- package/build/core/audit/report.js +1 -1
- package/build/core/bind-reconciliation.js +42 -23
- package/build/core/bind.js +182 -81
- package/build/core/call/call.js +6 -7
- package/build/core/call/context.js +43 -30
- package/build/core/call/execution.js +21 -6
- package/build/core/call/prompt.js +11 -42
- package/build/core/contract-carrier-runtime.js +209 -105
- package/build/core/contract-carrier.js +156 -73
- package/build/core/contract-view.js +42 -5
- package/build/core/contract.js +2 -1
- package/build/core/derived-replay.js +168 -35
- package/build/core/draft.js +9 -5
- package/build/core/forfeit.js +1 -1
- package/build/core/lifecycle-history.js +13 -0
- package/build/core/lifecycle-recovery.js +32 -27
- package/build/core/lifecycle-runner.js +14 -14
- package/build/core/log.js +1 -1
- package/build/core/outcome-base.js +2 -2
- package/build/core/projection/akuma-name.js +43 -0
- package/build/core/projection/generation/database.js +141 -79
- package/build/core/projection/generation/ledger.js +91 -0
- package/build/core/projection/generation/model.js +18 -13
- package/build/core/projection/generation/projection-generation-continuation.js +1 -1
- package/build/core/projection/generation/projection-generation-doorbell.js +88 -0
- package/build/core/projection/generation/projection-generation-execution.js +24 -0
- package/build/core/projection/generation/projection-generation-identity.js +4 -0
- package/build/core/projection/generation/projection-generation-launcher.js +72 -138
- package/build/core/projection/generation/projection-generation-process.js +16 -15
- package/build/core/projection/generation/projection-generation-runner.js +125 -79
- package/build/core/projection/generation/projection-generation-runtime.js +10 -61
- package/build/core/projection/generation/protocol.js +68 -0
- package/build/core/projection/generation/store.js +17 -7
- package/build/core/projection/generation/transitions.js +14 -14
- package/build/core/projection/index.js +5 -5
- package/build/core/projection/projection-core.js +6 -2
- package/build/core/projection/projection-execution-observer.js +1 -1
- package/build/core/projection/projection-history.js +7 -7
- package/build/core/projection/projection-kill.js +15 -6
- package/build/core/projection/projection-life-observer.js +8 -8
- package/build/core/projection/projection-mint.js +6 -8
- package/build/core/projection/projection-status-observation.js +77 -8
- package/build/core/projection/projection-status.js +151 -4
- package/build/core/projection/projection-wait.js +46 -27
- package/build/core/projection/projection-wake.js +27 -89
- package/build/core/projection/tell/launch-store.js +1 -1
- package/build/core/projection/tell/store.js +1 -1
- package/build/core/renew-build.js +204 -74
- package/build/core/renew-plan.js +34 -9
- package/build/core/renew-rewrite.js +56 -15
- package/build/core/renew.js +39 -35
- package/build/core/repository-ledger/accepted-fold-read.js +6 -3
- package/build/core/repository-ledger/codec.js +11 -5
- package/build/core/repository-ledger/current-state-store.js +13 -16
- package/build/core/repository-ledger/fold-repository.js +5 -6
- package/build/core/repository-ledger/identity.js +60 -0
- package/build/core/repository-ledger/inventory.js +3 -3
- package/build/core/repository-ledger/read-model.js +54 -6
- package/build/core/repository-ledger/write-transaction.js +9 -4
- package/build/core/run-control/connection-bound-close.js +41 -0
- package/build/core/run-control/detached-lease-bootstrap.js +208 -0
- package/build/core/run-control/detached-process.js +14 -0
- package/build/core/run-control/index.js +6 -0
- package/build/core/{process-group.js → run-control/process-tree.js} +1 -1
- package/build/core/run-control/runner-lease.js +172 -0
- package/build/core/{sqlite-process-lifetime-lock.js → run-control/sqlite-process-lifetime-lock.js} +4 -24
- package/build/core/seal.js +44 -32
- package/build/core/settlement/claim-delivery.js +22 -9
- package/build/core/settlement/claim.js +8 -6
- package/build/core/settlement/petition-claim-gates.js +9 -8
- package/build/core/settlement/petition-head-guard.js +5 -6
- package/build/core/settlement/petition-preview.js +21 -5
- package/build/core/settlement/petition.js +51 -17
- package/build/core/settlement/settlement.js +20 -15
- package/build/core/settlement/verdict.js +2 -2
- package/build/core/settlement/verification.js +492 -364
- package/build/core/status/board.js +184 -75
- package/build/core/status/drift.js +8 -10
- package/build/core/task/board.js +1 -0
- package/build/core/task/commands.js +25 -0
- package/build/core/task/compose.js +3 -2
- package/build/core/task/document.js +102 -39
- package/build/core/task/index.js +3 -3
- package/build/core/task/settlement-git.js +1 -4
- package/build/core/task/settlement-policy.js +27 -14
- package/build/core/task/source-board.js +1 -0
- package/build/core/task/task-bind-preparation.js +59 -9
- package/build/core/task/task-contract.js +2 -2
- package/build/core/task/task-store-repository.js +1 -0
- package/build/core/task/task.js +2 -2
- package/build/core/transcripts.js +4 -4
- package/build/core/verification-declaration.js +95 -0
- package/build/core/worktree-bootstrap.js +1 -1
- package/build/core/worktree-path.js +7 -11
- package/build/flow-error.js +9 -1
- package/build/generated/version.js +2 -2
- package/build/git/branches.js +41 -70
- package/build/git/commits.js +92 -21
- package/build/git/core.js +9 -161
- package/build/git/diff/preview.js +4 -4
- package/build/git/diff/read.js +4 -4
- package/build/git/diff/structured.js +5 -5
- package/build/git/process.js +229 -0
- package/build/git/refs.js +30 -5
- package/build/git/staging.js +39 -115
- package/build/git/streaming-batch.js +9 -16
- package/build/git/worktree.js +104 -91
- package/build/index.js +7 -0
- package/package.json +1 -1
- package/skills/keiyaku-akuma/SKILL.md +21 -20
- package/skills/keiyaku-task/SKILL.md +38 -4
- package/skills/keiyaku-workflow/SKILL.md +51 -11
- package/build/cli/commands/projection/revive/meta.js +0 -12
- package/build/core/projection/projection-life-protocol.js +0 -115
- package/build/core/projection/projection-runner-lock.js +0 -211
- package/build/core/projection/tell/database.js +0 -127
- package/build/core/settlement/verification-coordination.js +0 -305
- package/build/core/settlement/verification-supervisor.js +0 -275
|
@@ -20,6 +20,27 @@ function requireString(value, field) {
|
|
|
20
20
|
function optionalString(value, field) {
|
|
21
21
|
return value === undefined ? undefined : requireString(value, field);
|
|
22
22
|
}
|
|
23
|
+
function optionalCarrier(value) {
|
|
24
|
+
if (value === undefined)
|
|
25
|
+
return undefined;
|
|
26
|
+
if (!isObject(value) || value.path !== ".keiyaku/KEIYAKU.md" || !isObject(value.identity)) {
|
|
27
|
+
throw new Error("generation launch carrier must be a verified carrier coordinate");
|
|
28
|
+
}
|
|
29
|
+
const { identity } = value;
|
|
30
|
+
if (typeof identity.contractId !== "string" || identity.contractId.trim() === ""
|
|
31
|
+
|| typeof identity.contractHead !== "string" || identity.contractHead.trim() === ""
|
|
32
|
+
|| typeof identity.bodyDigest !== "string" || identity.bodyDigest.trim() === "") {
|
|
33
|
+
throw new Error("generation launch carrier identity must contain non-blank contractId, contractHead, and bodyDigest");
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
path: ".keiyaku/KEIYAKU.md",
|
|
37
|
+
identity: {
|
|
38
|
+
contractId: identity.contractId,
|
|
39
|
+
contractHead: identity.contractHead,
|
|
40
|
+
bodyDigest: identity.bodyDigest,
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
23
44
|
function requirePositiveSafeInteger(value, field) {
|
|
24
45
|
if (typeof value !== "number" || !Number.isSafeInteger(value) || value <= 0) {
|
|
25
46
|
throw new Error(`generation launch ${field} must be a positive safe integer`);
|
|
@@ -54,6 +75,7 @@ function parseLaunchInputs(launch) {
|
|
|
54
75
|
const context = optionalString(raw.context, "context");
|
|
55
76
|
const resumePath = optionalString(raw.resumePath, "resumePath");
|
|
56
77
|
const sourceArtifactId = optionalString(raw.sourceArtifactId, "sourceArtifactId");
|
|
78
|
+
const carrier = optionalCarrier(raw.carrier);
|
|
57
79
|
return {
|
|
58
80
|
prompt: requireString(raw.prompt, "prompt"),
|
|
59
81
|
cwd: requireString(raw.cwd, "cwd"),
|
|
@@ -69,6 +91,7 @@ function parseLaunchInputs(launch) {
|
|
|
69
91
|
...(raw.session ? { session: raw.session } : {}),
|
|
70
92
|
...(resumePath ? { resumePath } : {}),
|
|
71
93
|
...(sourceArtifactId ? { sourceArtifactId } : {}),
|
|
94
|
+
...(carrier ? { carrier } : {}),
|
|
72
95
|
};
|
|
73
96
|
}
|
|
74
97
|
function persistenceError(error) {
|
|
@@ -161,6 +184,7 @@ export function prepareProjectionGenerationExecution(projectionDirectory, launch
|
|
|
161
184
|
idleTimeoutMs: inputs.idleTimeoutMs,
|
|
162
185
|
executionTimeoutMs: inputs.executionTimeoutMs,
|
|
163
186
|
initialTurn: maximumValidEnvelopeTurn(projectionDirectory),
|
|
187
|
+
carrier: inputs.carrier,
|
|
164
188
|
}), providerLoaderPorts);
|
|
165
189
|
const observerController = createProjectionExecutionObserver({
|
|
166
190
|
projectionDirectory,
|
|
@@ -3,6 +3,7 @@ import * as path from "node:path";
|
|
|
3
3
|
import { ProjectionStateError } from "../../atomic-publish.js";
|
|
4
4
|
import { createUlid } from "../../ids.js";
|
|
5
5
|
const EXECUTION_ID_PATTERN = /^[0-9A-HJKMNP-TV-Z]{26}$/;
|
|
6
|
+
export const PROJECTION_RUNNER_LEASE_FILE = "leash";
|
|
6
7
|
export function createProjectionExecutionId(nowMs = Date.now()) {
|
|
7
8
|
return createUlid(nowMs, [...randomBytes(10)]);
|
|
8
9
|
}
|
|
@@ -15,3 +16,6 @@ export function executionStdioLogPath(projectionDirectory, executionId) {
|
|
|
15
16
|
assertProjectionExecutionId(executionId);
|
|
16
17
|
return path.join(projectionDirectory, `${executionId}.stdio.log`);
|
|
17
18
|
}
|
|
19
|
+
export function projectionRunnerLeasePath(projectionDirectory) {
|
|
20
|
+
return path.join(projectionDirectory, PROJECTION_RUNNER_LEASE_FILE);
|
|
21
|
+
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { setTimeout as delay } from "node:timers/promises";
|
|
2
|
+
import { PROJECTION_ADOPTION_TIMEOUT_DEFAULT_MS } from "../../../config/settings/knobs.js";
|
|
2
3
|
import { openProjectionGenerationStore } from "./store.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
import { prepareProjectionGenerationRunner, } from "./projection-generation-process.js";
|
|
5
|
+
import { acquireRunnerLease, observeRunnerLease, } from "../../run-control/runner-lease.js";
|
|
6
|
+
import { openDetachedLeaseBootstrap } from "../../run-control/detached-lease-bootstrap.js";
|
|
7
|
+
import { projectionRunnerLeasePath } from "./projection-generation-identity.js";
|
|
8
|
+
const PROJECTION_ADOPTION_POLL_MS = 20;
|
|
7
9
|
function errorDetail(error) {
|
|
8
10
|
return error instanceof Error ? error.message : String(error);
|
|
9
11
|
}
|
|
@@ -21,86 +23,39 @@ function recordDeadStartup(projectionDirectory, executionId, detail, stage, term
|
|
|
21
23
|
store.close();
|
|
22
24
|
}
|
|
23
25
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
};
|
|
41
|
-
const onError = (error) => {
|
|
42
|
-
failure = error;
|
|
43
|
-
for (const waiter of waiters.values())
|
|
44
|
-
waiter.reject(error);
|
|
45
|
-
waiters.clear();
|
|
46
|
-
};
|
|
47
|
-
const onExit = () => onError(new Error("runner exited during bootstrap"));
|
|
48
|
-
child.on("message", onMessage);
|
|
49
|
-
child.on("error", onError);
|
|
50
|
-
child.on("exit", onExit);
|
|
51
|
-
return {
|
|
52
|
-
waitFor(kind) {
|
|
53
|
-
if (seen.has(kind))
|
|
54
|
-
return Promise.resolve();
|
|
55
|
-
if (failure)
|
|
56
|
-
return Promise.reject(failure);
|
|
57
|
-
return new Promise((resolve, reject) => {
|
|
58
|
-
const timer = setTimeout(() => {
|
|
59
|
-
waiters.delete(kind);
|
|
60
|
-
reject(failure ?? new Error(`runner did not report ${kind} before startup timeout`));
|
|
61
|
-
}, PROJECTION_ADOPTION_TIMEOUT_MS);
|
|
62
|
-
waiters.set(kind, {
|
|
63
|
-
resolve: () => {
|
|
64
|
-
clearTimeout(timer);
|
|
65
|
-
waiters.delete(kind);
|
|
66
|
-
resolve();
|
|
67
|
-
},
|
|
68
|
-
reject: (error) => {
|
|
69
|
-
clearTimeout(timer);
|
|
70
|
-
waiters.delete(kind);
|
|
71
|
-
reject(error);
|
|
72
|
-
},
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
},
|
|
76
|
-
close() {
|
|
77
|
-
child.off("message", onMessage);
|
|
78
|
-
child.off("error", onError);
|
|
79
|
-
child.off("exit", onExit);
|
|
80
|
-
},
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
function sendBootstrapCommand(child, command) {
|
|
84
|
-
return new Promise((resolve, reject) => {
|
|
85
|
-
if (!child.send || !child.connected) {
|
|
86
|
-
reject(new Error("runner bootstrap channel is unavailable"));
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
child.send(command, (error) => error ? reject(error) : resolve());
|
|
90
|
-
});
|
|
26
|
+
const ADOPTION_TIMEOUT_DETAIL = "runner did not adopt before projection adoption timeout";
|
|
27
|
+
async function failAdoptionDeadline(projectionDirectory, executionId, child, now, terminateChild, detail = ADOPTION_TIMEOUT_DETAIL) {
|
|
28
|
+
try {
|
|
29
|
+
await terminateChild(child);
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
return { status: "failed", cause: "adoption", detail: `${detail}; runner termination could not be verified: ${errorDetail(error)}` };
|
|
33
|
+
}
|
|
34
|
+
const lease = observeRunnerLease(projectionRunnerLeasePath(projectionDirectory));
|
|
35
|
+
if (lease.state !== "released")
|
|
36
|
+
return {
|
|
37
|
+
status: "failed",
|
|
38
|
+
cause: "adoption",
|
|
39
|
+
detail: `${detail}; runner lock ${lease.state === "held" ? "remained held" : `could not be verified: ${lease.detail}`}`,
|
|
40
|
+
};
|
|
41
|
+
recordDeadStartup(projectionDirectory, executionId, detail, "adoption", new Date(now()).toISOString());
|
|
42
|
+
return { status: "failed", cause: "adoption", detail };
|
|
91
43
|
}
|
|
92
44
|
/** Spawn one committed launch and return only after its exact adoption row. */
|
|
93
45
|
export async function startProjectionGeneration(projectionDirectory, executionId, dependencies = {}) {
|
|
94
46
|
const now = dependencies.now ?? Date.now;
|
|
95
47
|
const sleep = dependencies.sleep ?? ((milliseconds) => delay(milliseconds));
|
|
96
|
-
const
|
|
48
|
+
const adoptionTimeoutMs = dependencies.adoptionTimeoutMs ?? PROJECTION_ADOPTION_TIMEOUT_DEFAULT_MS;
|
|
49
|
+
// Bootstrap IPC and durable adoption are one launch operation, not retries
|
|
50
|
+
// with independent startup allowances.
|
|
51
|
+
const deadline = now() + adoptionTimeoutMs;
|
|
97
52
|
let launchOwnership;
|
|
98
53
|
try {
|
|
99
54
|
launchOwnership = dependencies.launchOwnership
|
|
100
|
-
??
|
|
55
|
+
?? acquireRunnerLease(projectionRunnerLeasePath(projectionDirectory));
|
|
101
56
|
}
|
|
102
57
|
catch (error) {
|
|
103
|
-
if (
|
|
58
|
+
if (observeRunnerLease(projectionRunnerLeasePath(projectionDirectory)).state === "held") {
|
|
104
59
|
return { status: "pending" };
|
|
105
60
|
}
|
|
106
61
|
return {
|
|
@@ -109,11 +64,9 @@ export async function startProjectionGeneration(projectionDirectory, executionId
|
|
|
109
64
|
detail: `runner construction failed: ${errorDetail(error)}`,
|
|
110
65
|
};
|
|
111
66
|
}
|
|
112
|
-
let
|
|
113
|
-
let childPromoted = false;
|
|
114
|
-
let child;
|
|
67
|
+
let prepared;
|
|
115
68
|
try {
|
|
116
|
-
|
|
69
|
+
prepared = prepareProjectionGenerationRunner(projectionDirectory, executionId, dependencies);
|
|
117
70
|
}
|
|
118
71
|
catch (error) {
|
|
119
72
|
const detail = `runner spawn failed: ${errorDetail(error)}`;
|
|
@@ -121,60 +74,48 @@ export async function startProjectionGeneration(projectionDirectory, executionId
|
|
|
121
74
|
launchOwnership.close();
|
|
122
75
|
return { status: "failed", cause: "spawn", detail };
|
|
123
76
|
}
|
|
124
|
-
const
|
|
77
|
+
const opened = await openDetachedLeaseBootstrap({
|
|
78
|
+
coordinate: executionId,
|
|
79
|
+
leasePath: projectionRunnerLeasePath(projectionDirectory),
|
|
80
|
+
parentLease: launchOwnership,
|
|
81
|
+
launch: prepared.launch,
|
|
82
|
+
deadline,
|
|
83
|
+
now,
|
|
84
|
+
spawn: dependencies.spawn,
|
|
85
|
+
terminate: dependencies.terminateChild,
|
|
86
|
+
});
|
|
87
|
+
prepared.close();
|
|
88
|
+
if (opened.status === "failed") {
|
|
89
|
+
const detail = opened.stage === "spawn" ? `runner spawn failed: ${opened.detail}` : `runner construction failed: ${opened.detail}`;
|
|
90
|
+
recordDeadStartup(projectionDirectory, executionId, detail, opened.stage === "spawn" ? "spawn" : "adoption", new Date(now()).toISOString());
|
|
91
|
+
return { status: "failed", cause: opened.stage === "spawn" ? "spawn" : "adoption", detail };
|
|
92
|
+
}
|
|
93
|
+
let committed = true;
|
|
94
|
+
let commitFailure;
|
|
125
95
|
try {
|
|
126
|
-
|
|
127
|
-
if (dependencies.commitLaunch && !dependencies.commitLaunch()) {
|
|
128
|
-
await sendBootstrapCommand(child, { kind: "launch-rejected", executionId });
|
|
129
|
-
if (launcherAcquiredOwnership)
|
|
130
|
-
launchOwnership.close();
|
|
131
|
-
return { status: "failed", cause: "adoption", detail: "generation launch transaction was rejected" };
|
|
132
|
-
}
|
|
133
|
-
launchCommitted = true;
|
|
134
|
-
launchOwnership.releaseReservationForTransfer();
|
|
135
|
-
await sendBootstrapCommand(child, { kind: "promote-leash", executionId });
|
|
136
|
-
await bootstrap.waitFor("leash-acquired");
|
|
137
|
-
childPromoted = true;
|
|
138
|
-
await sendBootstrapCommand(child, { kind: "launch-committed", executionId });
|
|
139
|
-
try {
|
|
140
|
-
launchOwnership.close();
|
|
141
|
-
}
|
|
142
|
-
catch {
|
|
143
|
-
// The promoted child owns lifecycle truth; parent guard cleanup is diagnostic only.
|
|
144
|
-
}
|
|
96
|
+
committed = dependencies.commitLaunch?.() ?? true;
|
|
145
97
|
}
|
|
146
98
|
catch (error) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
try {
|
|
150
|
-
launchOwnership.close();
|
|
151
|
-
}
|
|
152
|
-
catch { }
|
|
153
|
-
return { status: "pending", child };
|
|
154
|
-
}
|
|
155
|
-
if (!launchCommitted) {
|
|
156
|
-
launchOwnership.close();
|
|
157
|
-
return { status: "failed", cause: "adoption", detail: `runner construction failed: ${errorDetail(error)}` };
|
|
158
|
-
}
|
|
159
|
-
try {
|
|
160
|
-
launchOwnership.reclaimReservation();
|
|
161
|
-
}
|
|
162
|
-
catch {
|
|
163
|
-
launchOwnership.close();
|
|
164
|
-
return { status: "pending", child };
|
|
165
|
-
}
|
|
166
|
-
try {
|
|
167
|
-
recordDeadStartup(projectionDirectory, executionId, `runner construction failed: ${errorDetail(error)}`, "adoption", new Date(now()).toISOString());
|
|
168
|
-
}
|
|
169
|
-
finally {
|
|
170
|
-
launchOwnership.close();
|
|
171
|
-
}
|
|
172
|
-
return { status: "failed", cause: "adoption", detail: `runner construction failed: ${errorDetail(error)}` };
|
|
99
|
+
committed = false;
|
|
100
|
+
commitFailure = error;
|
|
173
101
|
}
|
|
174
|
-
|
|
175
|
-
bootstrap.
|
|
102
|
+
if (!committed) {
|
|
103
|
+
const rejected = await opened.bootstrap.reject();
|
|
104
|
+
return {
|
|
105
|
+
status: "failed",
|
|
106
|
+
cause: "adoption",
|
|
107
|
+
detail: rejected.status === "failed"
|
|
108
|
+
? rejected.detail
|
|
109
|
+
: commitFailure ? `generation launch transaction failed: ${errorDetail(commitFailure)}` : "generation launch transaction was rejected",
|
|
110
|
+
};
|
|
176
111
|
}
|
|
177
|
-
const
|
|
112
|
+
const authorized = await opened.bootstrap.authorize();
|
|
113
|
+
if (authorized.status === "failed") {
|
|
114
|
+
const detail = now() >= deadline ? ADOPTION_TIMEOUT_DETAIL : `runner construction failed: ${authorized.detail}`;
|
|
115
|
+
recordDeadStartup(projectionDirectory, executionId, detail, "adoption", new Date(now()).toISOString());
|
|
116
|
+
return { status: "failed", cause: "adoption", detail };
|
|
117
|
+
}
|
|
118
|
+
const child = authorized.child;
|
|
178
119
|
for (;;) {
|
|
179
120
|
const observedAt = now();
|
|
180
121
|
const store = openProjectionGenerationStore(projectionDirectory);
|
|
@@ -202,19 +143,12 @@ export async function startProjectionGeneration(projectionDirectory, executionId
|
|
|
202
143
|
finally {
|
|
203
144
|
store.close();
|
|
204
145
|
}
|
|
146
|
+
if (observeRunnerLease(projectionRunnerLeasePath(projectionDirectory)).state === "held") {
|
|
147
|
+
return { status: "adopted", child };
|
|
148
|
+
}
|
|
205
149
|
if (observedAt >= deadline)
|
|
206
150
|
break;
|
|
207
151
|
await sleep(Math.max(0, Math.min(PROJECTION_ADOPTION_POLL_MS, deadline - observedAt)));
|
|
208
152
|
}
|
|
209
|
-
|
|
210
|
-
try {
|
|
211
|
-
const current = store.readCurrentGeneration();
|
|
212
|
-
if (current?.launch.executionId === executionId && current.adoption) {
|
|
213
|
-
return { status: "adopted", child };
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
finally {
|
|
217
|
-
store.close();
|
|
218
|
-
}
|
|
219
|
-
return { status: "pending", child };
|
|
153
|
+
return failAdoptionDeadline(projectionDirectory, executionId, child, now, dependencies.terminateChild ?? (await import("../../run-control/process-tree.js")).terminateProcessGroup);
|
|
220
154
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
-
import { spawn as nodeSpawn } from "node:child_process";
|
|
4
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { RUN_CONTROL_BOOTSTRAP_ENV } from "../../run-control/index.js";
|
|
5
5
|
import { executionStdioLogPath } from "./projection-generation-identity.js";
|
|
6
6
|
const MODULE_FILE = fileURLToPath(import.meta.url);
|
|
7
7
|
// Lives at src/core/projection/generation/ — four levels up is the package root.
|
|
@@ -17,9 +17,7 @@ export function projectionGenerationRunnerArgv(projectionDirectory, executionId)
|
|
|
17
17
|
executionId,
|
|
18
18
|
];
|
|
19
19
|
}
|
|
20
|
-
export
|
|
21
|
-
export function spawnProjectionGenerationRunner(projectionDirectory, executionId, dependencies = {}) {
|
|
22
|
-
const spawn = dependencies.spawn ?? nodeSpawn;
|
|
20
|
+
export function prepareProjectionGenerationRunner(projectionDirectory, executionId, dependencies = {}) {
|
|
23
21
|
const openSync = dependencies.openSync ?? fs.openSync;
|
|
24
22
|
const closeSync = dependencies.closeSync ?? fs.closeSync;
|
|
25
23
|
const absoluteDirectory = path.resolve(projectionDirectory);
|
|
@@ -29,18 +27,21 @@ export function spawnProjectionGenerationRunner(projectionDirectory, executionId
|
|
|
29
27
|
const logPath = executionStdioLogPath(absoluteDirectory, executionId);
|
|
30
28
|
const logFd = openSync(logPath, "a");
|
|
31
29
|
try {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
30
|
+
return {
|
|
31
|
+
launch: {
|
|
32
|
+
command,
|
|
33
|
+
args,
|
|
34
|
+
unref: true,
|
|
35
|
+
options: {
|
|
36
|
+
stdio: ["ignore", logFd, logFd, "ipc"],
|
|
37
|
+
env: { ...process.env, [RUN_CONTROL_BOOTSTRAP_ENV]: "1" },
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
close: () => closeSync(logFd),
|
|
41
|
+
};
|
|
42
42
|
}
|
|
43
|
-
|
|
43
|
+
catch (error) {
|
|
44
44
|
closeSync(logFd);
|
|
45
|
+
throw error;
|
|
45
46
|
}
|
|
46
47
|
}
|