@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
|
@@ -1,75 +1,80 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
|
+
import * as path from "node:path";
|
|
2
3
|
import { FlowError } from "../flow-error.js";
|
|
4
|
+
import { isCommitAncestor, readCommitCoordinate } from "../git/commits.js";
|
|
5
|
+
import { runGitProcess } from "../git/core.js";
|
|
3
6
|
import { replayCommittedDerivedState } from "./derived-replay.js";
|
|
4
7
|
import { EffectJournal } from "./outcome-base.js";
|
|
5
|
-
import { readRepositoryLifecycle } from "./repository-ledger/read-model.js";
|
|
6
|
-
import { REPOSITORY_LEDGER_REF } from "./repository-ledger/codec.js";
|
|
7
8
|
import { resolveAcceptedLifecycleTransaction } from "./repository-ledger/accepted-tail.js";
|
|
8
9
|
import { publishAuthoritativeTransaction } from "./repository-ledger/write-transaction.js";
|
|
9
|
-
import { cleanupContractWorkspace,
|
|
10
|
+
import { cleanupContractWorkspace, contractWorktreePathFromStableRoot, observeTerminalDeliveryRef, resolveContractCarrierTarget, stableRepoRoot } from "./worktree-path.js";
|
|
10
11
|
import { defaultCreateWorktree } from "./bind-workspace.js";
|
|
11
12
|
import { replayClaimTargetWorktree } from "./settlement/claim-delivery.js";
|
|
13
|
+
import { lifecycleTransactionId } from "./repository-ledger/identity.js";
|
|
12
14
|
let acceptedBind, acquireGate, carrierMatches, checkCancellation;
|
|
13
|
-
let observe, observedLedgerHead,
|
|
14
|
-
let
|
|
15
|
+
let expectedCarrierIdentity, observe, observedLedgerHead, publishCarrier;
|
|
16
|
+
let verifyCarrier, reconcileFoldedDeliveryWorktree, releaseGate, retireTerminalHereCarrier;
|
|
15
17
|
export function installCarrierRuntimePorts(ports) {
|
|
16
|
-
({ acceptedBind, acquireGate, carrierMatches, checkCancellation, observe, observedLedgerHead,
|
|
17
|
-
|
|
18
|
-
releaseGate,
|
|
18
|
+
({ acceptedBind, acquireGate, carrierMatches, checkCancellation, expectedCarrierIdentity, observe, observedLedgerHead,
|
|
19
|
+
publishCarrier, verifyCarrier, reconcileFoldedDeliveryWorktree,
|
|
20
|
+
releaseGate, retireTerminalHereCarrier } = ports);
|
|
19
21
|
}
|
|
20
22
|
export async function withContractCarrierSession(input, action) {
|
|
21
23
|
const stableRoot = await stableRepoRoot(input.cwd);
|
|
22
24
|
if (input.mode === "observe-only") {
|
|
23
25
|
checkCancellation(input.signal);
|
|
24
26
|
const { repository, lifecycle, target, pins } = await observe(stableRoot, input.contractId);
|
|
25
|
-
const projectedCarrier = target.status === "available" ? await
|
|
27
|
+
const projectedCarrier = target.status === "available" ? await verifyCarrier({ target: target.path, lifecycle }) : undefined;
|
|
26
28
|
const initialCarrier = target.status === "available"
|
|
27
|
-
? { status: carrierMatches(projectedCarrier
|
|
28
|
-
: { status: "target-unavailable",
|
|
29
|
-
return action(createSession(input.mode, stableRoot, input.contractId, repository, lifecycle, target, pins, initialCarrier, input.signal, input.contractViewWriter, input.renewDeliveryProjector, input.terminalWorkspaceCleanup));
|
|
29
|
+
? { status: projectedCarrier && carrierMatches(projectedCarrier) ? "current" : "stale", contractHead: lifecycle.contractHead, target: target.path }
|
|
30
|
+
: { status: "target-unavailable", contractHead: lifecycle.contractHead, reason: target.reason };
|
|
31
|
+
return action(createSession(input.mode, stableRoot, input.contractId, repository, lifecycle, target, pins, initialCarrier, input.signal, input.contractViewWriter, input.renewDeliveryProjector, input.terminalWorkspaceCleanup, input.transactionRunner));
|
|
30
32
|
}
|
|
31
|
-
//
|
|
32
|
-
// repository-wide carrier gate, then admit the snapshot only if the sole
|
|
33
|
-
// authoritative coordinate still names the same ledger tip under the gate.
|
|
33
|
+
// Publication coordination is scoped to the contract's proven physical target.
|
|
34
34
|
const preGateObserved = await observe(stableRoot, input.contractId);
|
|
35
|
-
const gate = await acquireGate(stableRoot, input.signal);
|
|
35
|
+
const gate = await acquireGate(stableRoot, input.contractId, preGateObserved.target.status === "available" ? preGateObserved.target.path : `unavailable:${input.contractId}`, input.signal);
|
|
36
36
|
try {
|
|
37
37
|
checkCancellation(input.signal);
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
? preGateObserved
|
|
41
|
-
: await observe(stableRoot, input.contractId);
|
|
42
|
-
const projectedCarrier = target.status === "available" ? await readCarrierIdentity(target.path) : null;
|
|
38
|
+
const { repository, lifecycle, target, pins } = preGateObserved;
|
|
39
|
+
const projectedCarrier = target.status === "available" ? await verifyCarrier({ target: target.path, lifecycle }) : undefined;
|
|
43
40
|
const initialCarrier = target.status === "available"
|
|
44
|
-
? { status: carrierMatches(projectedCarrier
|
|
45
|
-
: { status: "target-unavailable",
|
|
41
|
+
? { status: projectedCarrier && carrierMatches(projectedCarrier) ? "current" : "stale", contractHead: lifecycle.contractHead, target: target.path }
|
|
42
|
+
: { status: "target-unavailable", contractHead: lifecycle.contractHead, reason: target.reason };
|
|
46
43
|
checkCancellation(input.signal);
|
|
47
|
-
const session = createSession(input.mode, stableRoot, input.contractId, repository, lifecycle, target, pins, initialCarrier, input.signal, input.contractViewWriter, input.renewDeliveryProjector, input.terminalWorkspaceCleanup);
|
|
44
|
+
const session = createSession(input.mode, stableRoot, input.contractId, repository, lifecycle, target, pins, initialCarrier, input.signal, input.contractViewWriter, input.renewDeliveryProjector, input.terminalWorkspaceCleanup, input.transactionRunner);
|
|
48
45
|
return await action(session);
|
|
49
46
|
}
|
|
50
47
|
finally {
|
|
51
48
|
releaseGate(gate);
|
|
52
49
|
}
|
|
53
50
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
51
|
+
/** Bind-only carrier admission from the immutable Observe snapshot. */
|
|
52
|
+
export async function withObservedContractCarrierSession(input, action) {
|
|
53
|
+
checkCancellation(input.signal);
|
|
54
|
+
const gate = await acquireGate(input.stableRoot, input.contractId, input.observation.target.status === "available" ? input.observation.target.path : `unavailable:${input.contractId}`, input.signal);
|
|
55
|
+
try {
|
|
56
|
+
const { repository, lifecycle, target, pins } = input.observation;
|
|
57
|
+
const projectedCarrier = target.status === "available" ? await verifyCarrier({ target: target.path, lifecycle }) : undefined;
|
|
58
|
+
const initialCarrier = target.status === "available"
|
|
59
|
+
? { status: projectedCarrier && carrierMatches(projectedCarrier) ? "current" : "stale", contractHead: lifecycle.contractHead, target: target.path }
|
|
60
|
+
: { status: "target-unavailable", contractHead: lifecycle.contractHead, reason: target.reason };
|
|
61
|
+
return await action(createSession("mutation", input.stableRoot, input.contractId, repository, lifecycle, target, pins, initialCarrier, input.signal, input.contractViewWriter, undefined, input.terminalWorkspaceCleanup, undefined));
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
releaseGate(gate);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function createSession(mode, stableRoot, contractId, repository, lifecycle, target, pins, initialCarrier, signal, contractViewWriter, renewDeliveryProjector, terminalWorkspaceCleanup, transactionRunner) {
|
|
68
|
+
const admittedLedgerHead = pins.repositoryLedgerHead;
|
|
69
|
+
const admittedLifecycleTail = lifecycle.contractHead;
|
|
59
70
|
let currentLifecycle = lifecycle;
|
|
71
|
+
let currentObservation = { repository, lifecycle, target, pins };
|
|
60
72
|
let authoritativeTransactionSelected = false;
|
|
61
73
|
let effectsStarted = false;
|
|
62
74
|
let committedResult;
|
|
63
|
-
const journal = new EffectJournal(`${contractId}:${lifecycle.
|
|
75
|
+
const journal = new EffectJournal(`${contractId}:${lifecycle.contractHead}`);
|
|
64
76
|
async function finalizeCommitted(input) {
|
|
65
77
|
effectsStarted = true;
|
|
66
|
-
if (input.renewProjection) {
|
|
67
|
-
await journal.run({
|
|
68
|
-
effect: "delivery-worktree-projection",
|
|
69
|
-
idempotencyKey: `${contractId}:${input.head}:delivery-worktree-projection`,
|
|
70
|
-
run: async () => (renewDeliveryProjector ?? projectRenewDeliveryWorktree)(input.renewProjection.target, input.renewProjection.oldHead, input.renewProjection.newHead),
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
78
|
let resolvedTransaction;
|
|
74
79
|
await journal.run({
|
|
75
80
|
effect: "committed-ledger-reread",
|
|
@@ -81,15 +86,15 @@ function createSession(mode, stableRoot, contractId, repository, lifecycle, targ
|
|
|
81
86
|
if (accepted.status !== "accepted")
|
|
82
87
|
throw new FlowError("COMMITTED_LEDGER_REREAD_FAILED", `accepted ledger transaction ${input.head} could not be reread (${accepted.reason})`);
|
|
83
88
|
resolvedTransaction = accepted;
|
|
84
|
-
return { head: observed.lifecycle.
|
|
89
|
+
return { head: observed.lifecycle.contractHead };
|
|
85
90
|
},
|
|
86
91
|
});
|
|
87
92
|
journal.beginDerivedReplay();
|
|
88
93
|
const carrier = input.carrier === "retire"
|
|
89
|
-
? { status: "retired",
|
|
94
|
+
? { status: "retired", contractHead: currentLifecycle.contractHead, reason: "terminal" }
|
|
90
95
|
: target.status === "available"
|
|
91
|
-
? { status: "stale",
|
|
92
|
-
: { status: "target-unavailable",
|
|
96
|
+
? { status: "stale", contractHead: currentLifecycle.contractHead, target: target.path }
|
|
97
|
+
: { status: "target-unavailable", contractHead: currentLifecycle.contractHead, reason: target.reason };
|
|
93
98
|
committedResult = {
|
|
94
99
|
status: "committed",
|
|
95
100
|
head: input.head,
|
|
@@ -100,32 +105,35 @@ function createSession(mode, stableRoot, contractId, repository, lifecycle, targ
|
|
|
100
105
|
};
|
|
101
106
|
return committedResult;
|
|
102
107
|
}
|
|
103
|
-
async function publishRepository(transaction
|
|
108
|
+
async function publishRepository(transaction) {
|
|
104
109
|
if (mode !== "mutation")
|
|
105
110
|
throw new FlowError("INTERNAL_STATE", `carrier ${mode} session cannot publish repository ledger facts`);
|
|
106
111
|
if (!effectsStarted)
|
|
107
112
|
checkCancellation(signal);
|
|
108
113
|
let replayed;
|
|
109
114
|
let carrier = target.status === "available"
|
|
110
|
-
? { status: "stale",
|
|
111
|
-
: { status: "target-unavailable",
|
|
115
|
+
? { status: "stale", contractHead: currentLifecycle.contractHead, target: target.path }
|
|
116
|
+
: { status: "target-unavailable", contractHead: currentLifecycle.contractHead, reason: target.reason };
|
|
112
117
|
const replayPlan = {
|
|
113
118
|
steps: [
|
|
114
|
-
...additionalReplaySteps,
|
|
115
119
|
{
|
|
116
120
|
effect: "committed-ledger-reread",
|
|
117
|
-
dependencies:
|
|
121
|
+
dependencies: [],
|
|
118
122
|
run: async ({ ledgerHead }) => {
|
|
119
123
|
replayed = await observe(stableRoot, contractId);
|
|
120
|
-
if (replayed.
|
|
121
|
-
throw new FlowError("COMMITTED_LEDGER_REREAD_FAILED", `committed ledger head ${ledgerHead} could not be reread (observed ${replayed.
|
|
124
|
+
if (replayed.repository.observedLedgerHead !== ledgerHead) {
|
|
125
|
+
throw new FlowError("COMMITTED_LEDGER_REREAD_FAILED", `committed ledger head ${ledgerHead} could not be reread (observed ${replayed.repository.observedLedgerHead})`);
|
|
122
126
|
}
|
|
123
127
|
currentLifecycle = replayed.lifecycle;
|
|
128
|
+
currentObservation = replayed;
|
|
124
129
|
},
|
|
125
130
|
},
|
|
126
131
|
],
|
|
127
132
|
};
|
|
128
|
-
const publication = await publishAuthoritativeTransaction(stableRoot, transaction, {
|
|
133
|
+
const publication = await publishAuthoritativeTransaction(stableRoot, transaction, {
|
|
134
|
+
replayPlan,
|
|
135
|
+
...(transactionRunner ? { transactionRunner } : {}),
|
|
136
|
+
});
|
|
129
137
|
const result = publication.write;
|
|
130
138
|
if (result.family === "refused-before-effects") {
|
|
131
139
|
return {
|
|
@@ -166,9 +174,7 @@ function createSession(mode, stableRoot, contractId, repository, lifecycle, targ
|
|
|
166
174
|
authorityProof: result.authorityProof,
|
|
167
175
|
},
|
|
168
176
|
});
|
|
169
|
-
const replayDependencyEffects = {
|
|
170
|
-
"committed-ledger-reread": additionalReplaySteps.at(-1)?.effect ?? ledgerCasKey,
|
|
171
|
-
};
|
|
177
|
+
const replayDependencyEffects = { "committed-ledger-reread": ledgerCasKey };
|
|
172
178
|
const replayKeys = new Map();
|
|
173
179
|
for (const effect of result.replay.effects) {
|
|
174
180
|
const idempotencyKey = `${contractId}:${result.ledgerHead}:${effect.effect}`;
|
|
@@ -190,7 +196,7 @@ function createSession(mode, stableRoot, contractId, repository, lifecycle, targ
|
|
|
190
196
|
}
|
|
191
197
|
const observedTransaction = resolveAcceptedLifecycleTransaction({
|
|
192
198
|
read: replayed?.repository ?? (await observe(stableRoot, contractId)).repository,
|
|
193
|
-
transactionObjectId: result.ledgerHead,
|
|
199
|
+
transactionObjectId: lifecycleTransactionId(result.ledgerHead),
|
|
194
200
|
expectedContract: contractId,
|
|
195
201
|
});
|
|
196
202
|
if (observedTransaction.status !== "accepted") {
|
|
@@ -198,7 +204,7 @@ function createSession(mode, stableRoot, contractId, repository, lifecycle, targ
|
|
|
198
204
|
}
|
|
199
205
|
committedResult = {
|
|
200
206
|
status: "committed",
|
|
201
|
-
head: result.ledgerHead,
|
|
207
|
+
head: lifecycleTransactionId(result.ledgerHead),
|
|
202
208
|
transaction: observedTransaction,
|
|
203
209
|
carrier,
|
|
204
210
|
effects: journal.all(),
|
|
@@ -207,13 +213,16 @@ function createSession(mode, stableRoot, contractId, repository, lifecycle, targ
|
|
|
207
213
|
return committedResult;
|
|
208
214
|
}
|
|
209
215
|
async function replayDerivedState() {
|
|
210
|
-
const observed =
|
|
216
|
+
const observed = currentObservation.lifecycle.contractHead === currentLifecycle.contractHead
|
|
217
|
+
? currentObservation
|
|
218
|
+
: await observe(stableRoot, contractId);
|
|
219
|
+
currentObservation = observed;
|
|
211
220
|
currentLifecycle = observed.lifecycle;
|
|
212
221
|
const bind = acceptedBind(observed.lifecycle);
|
|
213
222
|
let committedTarget = observed.target;
|
|
214
223
|
let materialized = {
|
|
215
224
|
status: "failed",
|
|
216
|
-
|
|
225
|
+
contractHead: observed.lifecycle.contractHead,
|
|
217
226
|
cause: "carrier projection was not attempted",
|
|
218
227
|
};
|
|
219
228
|
await replayCommittedDerivedState({
|
|
@@ -227,7 +236,7 @@ function createSession(mode, stableRoot, contractId, repository, lifecycle, targ
|
|
|
227
236
|
committedTarget = await resolveContractCarrierTarget(stableRoot, contractId, bind);
|
|
228
237
|
let created = false;
|
|
229
238
|
if (committedTarget.status !== "available" && bind.data.workspace === "worktree") {
|
|
230
|
-
const worktreePath =
|
|
239
|
+
const worktreePath = contractWorktreePathFromStableRoot(stableRoot, contractId, bind);
|
|
231
240
|
try {
|
|
232
241
|
await fs.access(worktreePath);
|
|
233
242
|
throw Object.assign(new Error(`worktree path ${worktreePath} is occupied by an unowned filesystem entry`), { code: "BIND_WORKTREE_PATH_OCCUPIED", facts: { path: worktreePath, occupant: "unowned-filesystem-entry" } });
|
|
@@ -248,11 +257,23 @@ function createSession(mode, stableRoot, contractId, repository, lifecycle, targ
|
|
|
248
257
|
}
|
|
249
258
|
if (committedTarget.status !== "available")
|
|
250
259
|
throw new FlowError("CARRIER_TARGET_UNAVAILABLE", committedTarget.reason);
|
|
251
|
-
|
|
252
|
-
|
|
260
|
+
return { status: created ? "created" : "already-present", target: committedTarget };
|
|
261
|
+
},
|
|
262
|
+
reconcileRenewDelivery: async ({ lifecycle: accepted, target: acceptedTarget, transaction }) => {
|
|
263
|
+
if (acceptedTarget.status !== "available")
|
|
264
|
+
throw new FlowError("CARRIER_TARGET_UNAVAILABLE", acceptedTarget.reason);
|
|
265
|
+
const renew = transaction.entries.find((entry) => entry.kind === "renew");
|
|
266
|
+
if (!renew || renew.kind !== "renew") {
|
|
267
|
+
throw new FlowError("INTERNAL_STATE", `accepted renew transaction ${transaction.transactionObjectId} has no renew entry`);
|
|
268
|
+
}
|
|
269
|
+
if (renewDeliveryProjector) {
|
|
270
|
+
await renewDeliveryProjector(acceptedTarget, renew.data.oldHead, renew.data.newHead);
|
|
271
|
+
return "created";
|
|
272
|
+
}
|
|
273
|
+
return reconcileFoldedDeliveryWorktree({ targetRoot: acceptedTarget.root, lifecycle: accepted });
|
|
253
274
|
},
|
|
254
275
|
materializeContractView: async ({ lifecycle: accepted, target: acceptedTarget }) => {
|
|
255
|
-
const projected = await convergeCarrier(stableRoot, contractId,
|
|
276
|
+
const projected = await convergeCarrier(stableRoot, contractId, accepted, acceptedTarget, contractViewWriter);
|
|
256
277
|
materialized = projected;
|
|
257
278
|
if (projected.status === "failed")
|
|
258
279
|
throw new FlowError("CARRIER_MATERIALIZATION_FAILED", projected.cause);
|
|
@@ -263,14 +284,122 @@ function createSession(mode, stableRoot, contractId, repository, lifecycle, targ
|
|
|
263
284
|
retireContractView: async ({ lifecycle: accepted, target: acceptedTarget }) => {
|
|
264
285
|
return retireTerminalHereCarrier(accepted, acceptedTarget);
|
|
265
286
|
},
|
|
287
|
+
expectedCarrierIdentity,
|
|
288
|
+
inspectSurface: async ({ obligation }) => {
|
|
289
|
+
switch (obligation.surface) {
|
|
290
|
+
case "carrier": {
|
|
291
|
+
if (committedTarget.status !== "available")
|
|
292
|
+
return null;
|
|
293
|
+
const verification = await verifyCarrier({ target: committedTarget.path, lifecycle: observed.lifecycle });
|
|
294
|
+
return verification.status === "current" ? verification.actual : null;
|
|
295
|
+
}
|
|
296
|
+
case "carrier-retirement": {
|
|
297
|
+
if (committedTarget.status !== "available")
|
|
298
|
+
return obligation.expected;
|
|
299
|
+
try {
|
|
300
|
+
await fs.access(committedTarget.path);
|
|
301
|
+
return null;
|
|
302
|
+
}
|
|
303
|
+
catch (error) {
|
|
304
|
+
return error instanceof Error && "code" in error && error.code === "ENOENT" ? obligation.expected : null;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
case "narrow-ignore": {
|
|
308
|
+
if (committedTarget.status !== "available")
|
|
309
|
+
return null;
|
|
310
|
+
const expected = JSON.parse(obligation.expected.inventory);
|
|
311
|
+
const bytes = expected.length === 0 ? "" : `${expected.join("\n")}\n`;
|
|
312
|
+
try {
|
|
313
|
+
return await fs.readFile(path.join(committedTarget.root, ".gitignore"), "utf8") === bytes ? obligation.expected : null;
|
|
314
|
+
}
|
|
315
|
+
catch {
|
|
316
|
+
return null;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
case "worktree": {
|
|
320
|
+
if (observed.lifecycle.phase === "claimed" || observed.lifecycle.phase === "forfeited") {
|
|
321
|
+
return committedTarget.workspace === "here" || committedTarget.status === "unavailable"
|
|
322
|
+
? obligation.expected
|
|
323
|
+
: null;
|
|
324
|
+
}
|
|
325
|
+
if (committedTarget.status !== "available")
|
|
326
|
+
return null;
|
|
327
|
+
const [head, clean] = await Promise.all([
|
|
328
|
+
readCommitCoordinate(committedTarget.root, "HEAD"),
|
|
329
|
+
runGitProcess(committedTarget.root, ["status", "--porcelain=v1", "--untracked-files=no"]),
|
|
330
|
+
]);
|
|
331
|
+
const expectedHead = obligation.expected.deliveryHead;
|
|
332
|
+
return head.status === "ok" && clean.status === 0 && !clean.error && head.coordinate.commit === expectedHead && clean.stdout === ""
|
|
333
|
+
? obligation.expected
|
|
334
|
+
: null;
|
|
335
|
+
}
|
|
336
|
+
case "renew-delivery": {
|
|
337
|
+
if (committedTarget.status !== "available")
|
|
338
|
+
return null;
|
|
339
|
+
const [head, clean] = await Promise.all([
|
|
340
|
+
readCommitCoordinate(committedTarget.root, "HEAD"),
|
|
341
|
+
runGitProcess(committedTarget.root, ["status", "--porcelain=v1", "--untracked-files=no"]),
|
|
342
|
+
]);
|
|
343
|
+
const expectedHead = obligation.expected.newHead;
|
|
344
|
+
return head.status === "ok" && clean.status === 0 && !clean.error && head.coordinate.commit === expectedHead && clean.stdout === ""
|
|
345
|
+
? obligation.expected
|
|
346
|
+
: null;
|
|
347
|
+
}
|
|
348
|
+
case "delivery-ref": {
|
|
349
|
+
const projection = await observeTerminalDeliveryRef(stableRoot, contractId);
|
|
350
|
+
return projection.status === "aligned" ? obligation.expected : null;
|
|
351
|
+
}
|
|
352
|
+
case "target-worktree": {
|
|
353
|
+
const expected = obligation.expected;
|
|
354
|
+
const listed = await runGitProcess(stableRoot, ["worktree", "list", "--porcelain", "-z"]);
|
|
355
|
+
if (listed.error || listed.status !== 0)
|
|
356
|
+
return null;
|
|
357
|
+
let worktree;
|
|
358
|
+
for (const field of listed.stdout.split("\0")) {
|
|
359
|
+
if (field.startsWith("worktree "))
|
|
360
|
+
worktree = field.slice("worktree ".length);
|
|
361
|
+
if (field === `branch ${expected.target}` && worktree) {
|
|
362
|
+
const [head, clean, target] = await Promise.all([
|
|
363
|
+
readCommitCoordinate(worktree, "HEAD"),
|
|
364
|
+
runGitProcess(worktree, ["status", "--porcelain=v1", "--untracked-files=no"]),
|
|
365
|
+
readCommitCoordinate(stableRoot, expected.target),
|
|
366
|
+
]);
|
|
367
|
+
return head.status === "ok"
|
|
368
|
+
&& clean.status === 0 && !clean.error && clean.stdout === ""
|
|
369
|
+
&& target.status === "ok" && head.coordinate.commit === target.coordinate.commit
|
|
370
|
+
? obligation.expected
|
|
371
|
+
: null;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
return obligation.expected;
|
|
375
|
+
}
|
|
376
|
+
case "task-board": {
|
|
377
|
+
// An open bind projects the exact bind-base task bytes into the
|
|
378
|
+
// index and working tree. Terminal task settlement has distinct
|
|
379
|
+
// target/ref semantics and is deliberately reconciled below.
|
|
380
|
+
if (observed.lifecycle.phase === "claimed" || observed.lifecycle.phase === "forfeited")
|
|
381
|
+
return null;
|
|
382
|
+
const bind = acceptedBind(observed.lifecycle);
|
|
383
|
+
const taskIds = obligation.expected.taskIds;
|
|
384
|
+
const paths = taskIds.map((taskId) => `.keiyaku/tasks/${taskId}.md`);
|
|
385
|
+
const [cached, worktree] = await Promise.all([
|
|
386
|
+
runGitProcess(stableRoot, ["diff", "--quiet", "--cached", bind.data.base, "--", ...paths]),
|
|
387
|
+
runGitProcess(stableRoot, ["diff", "--quiet", "--", ...paths]),
|
|
388
|
+
]);
|
|
389
|
+
return cached.status === 0 && !cached.error && worktree.status === 0 && !worktree.error
|
|
390
|
+
? obligation.expected
|
|
391
|
+
: null;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
},
|
|
266
395
|
replayClaimTarget: (landing) => replayClaimTargetWorktree(stableRoot, landing),
|
|
267
396
|
cleanupWorkspace: terminalWorkspaceCleanup ?? cleanupContractWorkspace,
|
|
268
397
|
observeDeliveryRef: observeTerminalDeliveryRef,
|
|
269
398
|
},
|
|
270
399
|
});
|
|
271
|
-
const carrierProjection = journal.all().find((row) => row.effect === "carrier-projection");
|
|
400
|
+
const carrierProjection = journal.all().find((row) => row.effect === "carrier-retirement-projection");
|
|
272
401
|
if (["claimed", "forfeited"].includes(observed.lifecycle.phase) && carrierProjection?.status === "succeeded") {
|
|
273
|
-
materialized = { status: "retired",
|
|
402
|
+
materialized = { status: "retired", contractHead: observed.lifecycle.contractHead, reason: "terminal" };
|
|
274
403
|
}
|
|
275
404
|
return materialized;
|
|
276
405
|
}
|
|
@@ -302,6 +431,7 @@ function createSession(mode, stableRoot, contractId, repository, lifecycle, targ
|
|
|
302
431
|
checkCancellation(signal);
|
|
303
432
|
const observed = await observe(stableRoot, contractId);
|
|
304
433
|
currentLifecycle = observed.lifecycle;
|
|
434
|
+
currentObservation = observed;
|
|
305
435
|
if (!effectsStarted)
|
|
306
436
|
checkCancellation(signal);
|
|
307
437
|
return { lifecycle: observed.lifecycle, repository: observed.repository, target: observed.target, pins: observed.pins };
|
|
@@ -311,6 +441,7 @@ function createSession(mode, stableRoot, contractId, repository, lifecycle, targ
|
|
|
311
441
|
checkCancellation(signal);
|
|
312
442
|
const observed = await observe(stableRoot, contractId);
|
|
313
443
|
currentLifecycle = observed.lifecycle;
|
|
444
|
+
currentObservation = observed;
|
|
314
445
|
if (!effectsStarted)
|
|
315
446
|
checkCancellation(signal);
|
|
316
447
|
return { lifecycle: observed.lifecycle, repository: observed.repository, target: observed.target, pins: observed.pins };
|
|
@@ -339,15 +470,7 @@ function createSession(mode, stableRoot, contractId, repository, lifecycle, targ
|
|
|
339
470
|
throw new FlowError("CONCURRENT_MODIFICATION", `authoritative transaction ledger CAS does not match immutable carrier admission ${contractId}`);
|
|
340
471
|
if (transaction.acceptedLifecycleTail !== admittedLifecycleTail)
|
|
341
472
|
throw new FlowError("CONCURRENT_MODIFICATION", `authoritative transaction lifecycle tail does not match immutable carrier admission ${contractId}`);
|
|
342
|
-
|
|
343
|
-
const replaySteps = transaction.verb === "renew" && accepted.kind === "renew" && "delivery" in transaction
|
|
344
|
-
? [{
|
|
345
|
-
effect: "delivery-worktree-projection",
|
|
346
|
-
dependencies: [],
|
|
347
|
-
run: async () => (renewDeliveryProjector ?? projectRenewDeliveryWorktree)(target, transaction.delivery.expectedOid, accepted.data.newHead),
|
|
348
|
-
}]
|
|
349
|
-
: [];
|
|
350
|
-
return publishRepository(transaction, replaySteps);
|
|
473
|
+
return publishRepository(transaction);
|
|
351
474
|
},
|
|
352
475
|
async replayCommitted(input) {
|
|
353
476
|
if (mode !== "mutation")
|
|
@@ -357,35 +480,21 @@ function createSession(mode, stableRoot, contractId, repository, lifecycle, targ
|
|
|
357
480
|
if (accepted.status !== "accepted")
|
|
358
481
|
throw new FlowError("CONCURRENT_MODIFICATION", `cannot replay unproved transaction ${input.transactionObjectId}`);
|
|
359
482
|
const renew = accepted.entries.find((entry) => entry.kind === "renew");
|
|
360
|
-
|
|
483
|
+
const deliveryDescendsFromRenew = renew && observed.pins.delivery.head
|
|
484
|
+
? await isCommitAncestor(stableRoot, renew.data.newHead, observed.pins.delivery.head)
|
|
485
|
+
: false;
|
|
486
|
+
if (renew && (observed.lifecycle.pins.base !== renew.data.newBase
|
|
487
|
+
|| observed.lifecycle.pins.delivery !== renew.data.newHead
|
|
488
|
+
|| !deliveryDescendsFromRenew)) {
|
|
361
489
|
throw new FlowError("CONCURRENT_MODIFICATION", `accepted renew transaction ${input.transactionObjectId} no longer matches current coordinates`);
|
|
362
490
|
}
|
|
363
491
|
currentLifecycle = observed.lifecycle;
|
|
364
|
-
return finalizeCommitted({
|
|
365
|
-
head: input.transactionObjectId,
|
|
366
|
-
...(renew ? { renewProjection: { target: observed.target, oldHead: renew.data.oldHead, newHead: renew.data.newHead } } : {}),
|
|
367
|
-
carrier: input.carrier,
|
|
368
|
-
});
|
|
492
|
+
return finalizeCommitted({ head: input.transactionObjectId, carrier: input.carrier });
|
|
369
493
|
},
|
|
370
494
|
};
|
|
371
495
|
}
|
|
372
|
-
async function convergeCarrier(stableRoot, contractId,
|
|
373
|
-
|
|
374
|
-
let lifecycle = initialLifecycle;
|
|
375
|
-
let target = initialTarget;
|
|
376
|
-
for (let attempt = 0; attempt < 4; attempt += 1) {
|
|
377
|
-
const published = await publishCarrier({ stableRoot, contractId, repository, lifecycle, target, contractViewWriter });
|
|
378
|
-
if (published.status !== "failed")
|
|
379
|
-
return published;
|
|
380
|
-
const activeHead = (await readRepositoryLifecycle(stableRoot)).observedLedgerHead;
|
|
381
|
-
if (activeHead === lifecycle.observedLedgerHead)
|
|
382
|
-
return published;
|
|
383
|
-
const observed = await observe(stableRoot, contractId);
|
|
384
|
-
repository = observed.repository;
|
|
385
|
-
lifecycle = observed.lifecycle;
|
|
386
|
-
target = observed.target;
|
|
387
|
-
}
|
|
388
|
-
return { status: "failed", ledgerHead: lifecycle.observedLedgerHead, ...(target.status === "available" ? { target: target.path } : {}), cause: "carrier convergence did not reach a stable ledger head" };
|
|
496
|
+
async function convergeCarrier(stableRoot, contractId, lifecycle, target, contractViewWriter) {
|
|
497
|
+
return publishCarrier({ stableRoot, contractId, lifecycle, target, contractViewWriter });
|
|
389
498
|
}
|
|
390
499
|
export async function convergeContractCarrier(input) {
|
|
391
500
|
return withContractCarrierSession({ ...input, mode: "observe-only" }, async (session) => ({ lifecycle: session.lifecycle, repository: session.repository, target: session.target, carrier: session.initialCarrier }));
|
|
@@ -394,15 +503,10 @@ export async function convergeContractCarrier(input) {
|
|
|
394
503
|
export async function reconcileContractCarrierProjection(input) {
|
|
395
504
|
checkCancellation(input.signal);
|
|
396
505
|
const stableRoot = await stableRepoRoot(input.cwd);
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
: await
|
|
402
|
-
|
|
403
|
-
lifecycle: reconciled.lifecycle,
|
|
404
|
-
repository: reconciled.repository,
|
|
405
|
-
target: reconciled.target,
|
|
406
|
-
carrier,
|
|
407
|
-
};
|
|
506
|
+
return withContractCarrierSession({ cwd: stableRoot, contractId: input.contractId, mode: "mutation", signal: input.signal, contractViewWriter: input.contractViewWriter }, async (session) => ({
|
|
507
|
+
lifecycle: session.lifecycle,
|
|
508
|
+
repository: session.repository,
|
|
509
|
+
target: session.target,
|
|
510
|
+
carrier: await convergeCarrier(stableRoot, input.contractId, session.lifecycle, session.target, input.contractViewWriter),
|
|
511
|
+
}));
|
|
408
512
|
}
|