@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
|
@@ -5,6 +5,9 @@ import { FlowError } from "../flow-error.js";
|
|
|
5
5
|
import { runGitProcess } from "../git/core.js";
|
|
6
6
|
import { KEIYAKU_FILE } from "../keiyaku.js";
|
|
7
7
|
import { inventoryIgnorePatternsForWorktree } from "./repository-ledger/inventory.js";
|
|
8
|
+
import { isJsonObject } from "./outcome-base.js";
|
|
9
|
+
import { carrierRetirementInputIdentity, deliveryRefInputIdentity, narrowIgnoreInputIdentity, renewDeliveryInputIdentity, sameInputIdentity, taskBoardInputIdentity, targetWorktreeInputIdentity, worktreeInputIdentity, } from "./repository-ledger/identity.js";
|
|
10
|
+
import { resolveAcceptedLifecycleTransaction, } from "./repository-ledger/accepted-tail.js";
|
|
8
11
|
import { readTaskBoardGeneration, reconcileTaskForfeit, replayClaimTasks, replayCommittedTaskBind, } from "./task/index.js";
|
|
9
12
|
function terminalEntry(model) {
|
|
10
13
|
return [...model.history].reverse().find(({ entry }) => (entry.kind === "claim" || entry.kind === "forfeit"));
|
|
@@ -13,10 +16,101 @@ function receipt(model, surface, status, facts) {
|
|
|
13
16
|
return {
|
|
14
17
|
surface,
|
|
15
18
|
status,
|
|
16
|
-
authoritativeHead: model.
|
|
19
|
+
authoritativeHead: model.contractHead,
|
|
17
20
|
...(facts ? { facts } : {}),
|
|
18
21
|
};
|
|
19
22
|
}
|
|
23
|
+
function currentTransaction(repository, model) {
|
|
24
|
+
const node = repository.topology.nodes.find((candidate) => (candidate.type === "transaction"
|
|
25
|
+
&& candidate.contract === model.contract
|
|
26
|
+
&& candidate.finalEntryObjectId === model.contractHead));
|
|
27
|
+
if (!node || node.type !== "transaction") {
|
|
28
|
+
throw new FlowError("INTERNAL_STATE", `contract ${model.contract} has no accepted transaction for ${model.contractHead}`);
|
|
29
|
+
}
|
|
30
|
+
const accepted = resolveAcceptedLifecycleTransaction({
|
|
31
|
+
read: repository,
|
|
32
|
+
transactionObjectId: node.objectId,
|
|
33
|
+
expectedContract: model.contract,
|
|
34
|
+
});
|
|
35
|
+
if (accepted.status !== "accepted") {
|
|
36
|
+
throw new FlowError("INTERNAL_STATE", `contract ${model.contract} transaction ${node.objectId} is not replayable (${accepted.reason})`);
|
|
37
|
+
}
|
|
38
|
+
return accepted;
|
|
39
|
+
}
|
|
40
|
+
function obligation(transactionObjectId, surface, expected) {
|
|
41
|
+
return {
|
|
42
|
+
transactionObjectId,
|
|
43
|
+
surface,
|
|
44
|
+
expected,
|
|
45
|
+
idempotencyKey: `${transactionObjectId}:${surface}`,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Purely derive every required surface for the accepted transaction that
|
|
50
|
+
* advanced this folded contract head. No journal, carrier bytes, or filesystem
|
|
51
|
+
* observation participates in this calculation.
|
|
52
|
+
*/
|
|
53
|
+
export function deriveReplayObligations(input) {
|
|
54
|
+
const { repository, lifecycle: model, target, expectedCarrierIdentity } = input;
|
|
55
|
+
const transaction = input.transaction ?? currentTransaction(repository, model);
|
|
56
|
+
const base = { contractId: model.contract, contractHead: model.contractHead };
|
|
57
|
+
const terminal = terminalEntry(model);
|
|
58
|
+
const required = [];
|
|
59
|
+
const taskIds = model.resources.tasks.map(({ taskId }) => taskId);
|
|
60
|
+
if (taskIds.length > 0) {
|
|
61
|
+
required.push(obligation(transaction.transactionObjectId, "task-board", taskBoardInputIdentity({
|
|
62
|
+
...base, taskIds, phase: model.phase,
|
|
63
|
+
})));
|
|
64
|
+
}
|
|
65
|
+
if (terminal) {
|
|
66
|
+
required.push(obligation(transaction.transactionObjectId, "carrier-retirement", carrierRetirementInputIdentity({
|
|
67
|
+
...base,
|
|
68
|
+
target: target.status === "available" ? target.path : `${target.workspace}:${target.reason}`,
|
|
69
|
+
})), obligation(transaction.transactionObjectId, "worktree", worktreeInputIdentity({
|
|
70
|
+
...base,
|
|
71
|
+
deliveryHead: model.pins.delivery,
|
|
72
|
+
})), obligation(transaction.transactionObjectId, "delivery-ref", deliveryRefInputIdentity({
|
|
73
|
+
...base, deliveryRef: model.resources.deliveryRef,
|
|
74
|
+
})));
|
|
75
|
+
if (terminal.entry.kind === "claim" && terminal.entry.data && typeof terminal.entry.data.target === "string") {
|
|
76
|
+
required.push(obligation(transaction.transactionObjectId, "target-worktree", targetWorktreeInputIdentity({
|
|
77
|
+
...base,
|
|
78
|
+
target: terminal.entry.data.target,
|
|
79
|
+
targetHead: terminal.entry.data.targetHead,
|
|
80
|
+
mergeCommit: terminal.entry.data.mergeCommit,
|
|
81
|
+
})));
|
|
82
|
+
}
|
|
83
|
+
if (target.status === "available") {
|
|
84
|
+
required.push(obligation(transaction.transactionObjectId, "narrow-ignore", narrowIgnoreInputIdentity({
|
|
85
|
+
...base,
|
|
86
|
+
inventory: JSON.stringify(inventoryIgnorePatternsForWorktree(repository, model)),
|
|
87
|
+
occupancy: `${target.workspace}:${target.root}`,
|
|
88
|
+
})));
|
|
89
|
+
}
|
|
90
|
+
return required;
|
|
91
|
+
}
|
|
92
|
+
const deliveryHead = model.pins.delivery;
|
|
93
|
+
if (deliveryHead !== null) {
|
|
94
|
+
required.push(obligation(transaction.transactionObjectId, "worktree", worktreeInputIdentity({ ...base, deliveryHead })));
|
|
95
|
+
}
|
|
96
|
+
const patterns = inventoryIgnorePatternsForWorktree(repository, model);
|
|
97
|
+
required.push(obligation(transaction.transactionObjectId, "narrow-ignore", narrowIgnoreInputIdentity({
|
|
98
|
+
...base,
|
|
99
|
+
inventory: JSON.stringify(patterns),
|
|
100
|
+
occupancy: `${target.workspace}:${target.status === "available" ? target.root : target.reason}`,
|
|
101
|
+
})), obligation(transaction.transactionObjectId, "carrier", expectedCarrierIdentity));
|
|
102
|
+
if (transaction.verb === "renew") {
|
|
103
|
+
const renew = transaction.entries.find((entry) => entry.kind === "renew");
|
|
104
|
+
if (renew?.kind === "renew") {
|
|
105
|
+
required.push(obligation(transaction.transactionObjectId, "renew-delivery", renewDeliveryInputIdentity({
|
|
106
|
+
...base,
|
|
107
|
+
oldHead: renew.data.oldHead,
|
|
108
|
+
newHead: renew.data.newHead,
|
|
109
|
+
})));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return required;
|
|
113
|
+
}
|
|
20
114
|
async function gitExit(cwd, args) {
|
|
21
115
|
const result = await runGitProcess(cwd, args);
|
|
22
116
|
if (result.error || (result.status !== 0 && result.status !== 1)) {
|
|
@@ -83,14 +177,9 @@ async function replayTaskBoard(cwd, model, adapters) {
|
|
|
83
177
|
taskIds,
|
|
84
178
|
updatedAt: terminal.entry.at,
|
|
85
179
|
});
|
|
86
|
-
const warnings = await adapters.replayClaimTarget({
|
|
87
|
-
target: terminal.entry.data.target,
|
|
88
|
-
targetHead: terminal.entry.data.targetHead,
|
|
89
|
-
mergeCommit: replay.commit,
|
|
90
|
-
});
|
|
91
180
|
return {
|
|
92
181
|
status: taskIds.length === 0 ? "already-present" : replay.worktree === "ref-only" ? "ref-only" : "created",
|
|
93
|
-
facts: { taskCount: replay.taskCount, commit: replay.commit, worktree: replay.worktree
|
|
182
|
+
facts: { taskCount: replay.taskCount, commit: replay.commit, worktree: replay.worktree },
|
|
94
183
|
};
|
|
95
184
|
}
|
|
96
185
|
if (terminal?.entry.kind === "forfeit") {
|
|
@@ -113,10 +202,10 @@ async function replayTaskBoard(cwd, model, adapters) {
|
|
|
113
202
|
const status = await replayCommittedTaskBind(cwd, bind.data.base, taskIds);
|
|
114
203
|
return { status, facts: { taskCount: taskIds.length } };
|
|
115
204
|
}
|
|
116
|
-
function recordSurface(journal,
|
|
205
|
+
function recordSurface(journal, replay, run) {
|
|
117
206
|
return journal.run({
|
|
118
|
-
effect: `${surface}-projection`,
|
|
119
|
-
idempotencyKey:
|
|
207
|
+
effect: `${replay.surface}-projection`,
|
|
208
|
+
idempotencyKey: replay.idempotencyKey,
|
|
120
209
|
run: async () => structuredClone(await run()),
|
|
121
210
|
});
|
|
122
211
|
}
|
|
@@ -124,29 +213,65 @@ function recordSurface(journal, model, surface, run) {
|
|
|
124
213
|
export async function replayCommittedDerivedState(input) {
|
|
125
214
|
const { lifecycle: model, journal } = input;
|
|
126
215
|
const terminal = terminalEntry(model);
|
|
216
|
+
const transaction = input.transaction ?? currentTransaction(input.repository, model);
|
|
217
|
+
const expectedCarrierIdentity = input.adapters.expectedCarrierIdentity(model);
|
|
218
|
+
if (expectedCarrierIdentity.contractId !== model.contract
|
|
219
|
+
|| expectedCarrierIdentity.contractHead !== model.contractHead) {
|
|
220
|
+
throw new FlowError("INTERNAL_STATE", "carrier identity semantics did not derive the folded contract coordinate");
|
|
221
|
+
}
|
|
222
|
+
const obligations = new Map(deriveReplayObligations({
|
|
223
|
+
...input,
|
|
224
|
+
transaction,
|
|
225
|
+
expectedCarrierIdentity,
|
|
226
|
+
}).map((item) => [item.surface, item]));
|
|
127
227
|
let projectedTarget = input.target;
|
|
128
228
|
const receipts = [];
|
|
129
|
-
const run = async (surface, operation) => {
|
|
130
|
-
const
|
|
229
|
+
const run = async (surface, operation, current) => {
|
|
230
|
+
const replay = obligations.get(surface);
|
|
231
|
+
if (!replay)
|
|
232
|
+
return;
|
|
233
|
+
const row = await recordSurface(journal, replay, async () => {
|
|
234
|
+
const actual = await input.adapters.inspectSurface({ obligation: replay });
|
|
235
|
+
if (sameInputIdentity(actual, replay.expected)) {
|
|
236
|
+
return current?.() ?? receipt(model, surface, "already-present", { inputIdentity: replay.expected });
|
|
237
|
+
}
|
|
238
|
+
return operation();
|
|
239
|
+
});
|
|
131
240
|
if (row.status === "succeeded")
|
|
132
241
|
receipts.push(row.receipt);
|
|
133
242
|
};
|
|
134
|
-
await run("status", async () => receipt(model, "status", "already-present", { phase: model.phase }));
|
|
135
|
-
await run("registry", async () => receipt(model, "registry", terminal ? "retired" : "already-present", {
|
|
136
|
-
source: "repository-ledger-fold",
|
|
137
|
-
effectiveScope: [...model.effectiveScope],
|
|
138
|
-
}));
|
|
139
243
|
await run("task-board", async () => {
|
|
140
244
|
const replay = await replayTaskBoard(input.cwd, model, input.adapters);
|
|
141
245
|
return receipt(model, "task-board", replay.status, replay.facts);
|
|
142
246
|
});
|
|
247
|
+
if (terminal?.entry.kind === "claim") {
|
|
248
|
+
const claim = terminal.entry;
|
|
249
|
+
await run("target-worktree", async () => {
|
|
250
|
+
const taskRow = journal.all().find((row) => row.effect === "task-board-projection" && row.status === "succeeded");
|
|
251
|
+
const taskFacts = taskRow?.status === "succeeded" ? taskRow.receipt?.facts : undefined;
|
|
252
|
+
const commit = isJsonObject(taskFacts) && typeof taskFacts.commit === "string"
|
|
253
|
+
? taskFacts.commit
|
|
254
|
+
: claim.data.mergeCommit;
|
|
255
|
+
const warnings = await input.adapters.replayClaimTarget({
|
|
256
|
+
target: claim.data.target,
|
|
257
|
+
targetHead: claim.data.targetHead,
|
|
258
|
+
mergeCommit: commit,
|
|
259
|
+
});
|
|
260
|
+
return receipt(model, "target-worktree", warnings.length === 0 ? "created" : "mismatch", {
|
|
261
|
+
target: claim.data.target,
|
|
262
|
+
targetHead: claim.data.targetHead,
|
|
263
|
+
mergeCommit: commit,
|
|
264
|
+
warnings: [...warnings],
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
}
|
|
143
268
|
if (terminal) {
|
|
144
269
|
let carrierRetired = false;
|
|
145
|
-
await run("carrier", async () => {
|
|
270
|
+
await run("carrier-retirement", async () => {
|
|
146
271
|
const status = await input.adapters.retireContractView({ lifecycle: model, target: input.target });
|
|
147
|
-
|
|
148
|
-
return receipt(model, "carrier", status);
|
|
272
|
+
return receipt(model, "carrier-retirement", status);
|
|
149
273
|
});
|
|
274
|
+
carrierRetired = journal.all().some((row) => (row.effect === "carrier-retirement-projection" && row.status === "succeeded"));
|
|
150
275
|
await run("narrow-ignore", async () => {
|
|
151
276
|
if (!carrierRetired) {
|
|
152
277
|
throw new FlowError("CARRIER_MATERIALIZATION_FAILED", "cannot retire narrow ignore before carrier retirement");
|
|
@@ -159,19 +284,23 @@ export async function replayCommittedDerivedState(input) {
|
|
|
159
284
|
});
|
|
160
285
|
await run("worktree", async () => {
|
|
161
286
|
const cleanup = await input.adapters.cleanupWorkspace(input.cwd, model);
|
|
162
|
-
if (cleanup.worktreeRemoveFailure)
|
|
163
|
-
throw new FlowError("INTERNAL_STATE", cleanup.worktreeRemoveFailure.reason);
|
|
164
287
|
return receipt(model, "worktree", cleanup.absent.includes("worktree") ? "already-present" : "retired", {
|
|
165
288
|
absent: cleanup.absent,
|
|
166
289
|
...(cleanup.removedWorktreePath ? { removedWorktreePath: cleanup.removedWorktreePath } : {}),
|
|
290
|
+
...(cleanup.worktreeRemoveFailure ? { worktreeRemoveFailure: cleanup.worktreeRemoveFailure } : {}),
|
|
167
291
|
});
|
|
168
|
-
});
|
|
292
|
+
}, () => receipt(model, "worktree", "already-present", { absent: ["worktree"] }));
|
|
169
293
|
await run("delivery-ref", async () => {
|
|
170
294
|
const observation = await input.adapters.observeDeliveryRef(input.cwd, model.contract);
|
|
171
295
|
return observation.status === "aligned"
|
|
172
296
|
? receipt(model, "delivery-ref", "already-present", observation)
|
|
173
297
|
: receipt(model, "delivery-ref", "mismatch", observation);
|
|
174
|
-
})
|
|
298
|
+
}, () => receipt(model, "delivery-ref", "already-present", {
|
|
299
|
+
status: "aligned",
|
|
300
|
+
ref: model.resources.deliveryRef,
|
|
301
|
+
expected: "absent",
|
|
302
|
+
observed: "absent",
|
|
303
|
+
}));
|
|
175
304
|
return receipts;
|
|
176
305
|
}
|
|
177
306
|
await run("worktree", async () => {
|
|
@@ -179,6 +308,18 @@ export async function replayCommittedDerivedState(input) {
|
|
|
179
308
|
projectedTarget = replay.target;
|
|
180
309
|
return receipt(model, "worktree", replay.status);
|
|
181
310
|
});
|
|
311
|
+
if (obligations.has("renew-delivery")) {
|
|
312
|
+
await run("renew-delivery", async () => {
|
|
313
|
+
if (transaction.verb !== "renew")
|
|
314
|
+
throw new FlowError("INTERNAL_STATE", "renew delivery obligation lacks an accepted renew transaction");
|
|
315
|
+
const renew = transaction.entries.find((entry) => entry.kind === "renew");
|
|
316
|
+
if (!renew || renew.kind !== "renew")
|
|
317
|
+
throw new FlowError("INTERNAL_STATE", "accepted renew transaction lacks a renew entry");
|
|
318
|
+
if (projectedTarget.status !== "available")
|
|
319
|
+
throw new FlowError("CARRIER_TARGET_UNAVAILABLE", projectedTarget.reason);
|
|
320
|
+
return receipt(model, "renew-delivery", await input.adapters.reconcileRenewDelivery({ lifecycle: model, target: projectedTarget, transaction }), { oldHead: renew.data.oldHead, newHead: renew.data.newHead });
|
|
321
|
+
});
|
|
322
|
+
}
|
|
182
323
|
await run("narrow-ignore", async () => {
|
|
183
324
|
if (projectedTarget.status !== "available")
|
|
184
325
|
throw new FlowError("CARRIER_TARGET_UNAVAILABLE", projectedTarget.reason);
|
|
@@ -187,21 +328,13 @@ export async function replayCommittedDerivedState(input) {
|
|
|
187
328
|
patterns,
|
|
188
329
|
});
|
|
189
330
|
});
|
|
190
|
-
|
|
191
|
-
let viewProjected = false;
|
|
192
|
-
await run("contract-view", async () => {
|
|
331
|
+
await run("carrier", async () => {
|
|
193
332
|
const incomplete = journal.all().find((row) => row.status !== "succeeded");
|
|
194
333
|
if (incomplete) {
|
|
195
334
|
throw new FlowError("CARRIER_MATERIALIZATION_FAILED", `cannot advance carrier checkpoint while ${incomplete.effect} is ${incomplete.status}`);
|
|
196
335
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
return receipt(model, "contract-view", viewStatus, { path: projectedTarget.status === "available" ? path.join(projectedTarget.root, KEIYAKU_FILE) : null });
|
|
200
|
-
});
|
|
201
|
-
await run("carrier", async () => {
|
|
202
|
-
if (!viewProjected)
|
|
203
|
-
throw new FlowError("CARRIER_MATERIALIZATION_FAILED", "contract view was not materialized");
|
|
204
|
-
return receipt(model, "carrier", viewStatus, { checkpoint: model.observedLedgerHead });
|
|
336
|
+
const status = await input.adapters.materializeContractView({ lifecycle: model, target: projectedTarget });
|
|
337
|
+
return receipt(model, "carrier", status, { path: projectedTarget.status === "available" ? path.join(projectedTarget.root, KEIYAKU_FILE) : null });
|
|
205
338
|
});
|
|
206
339
|
return receipts;
|
|
207
340
|
}
|
package/build/core/draft.js
CHANGED
|
@@ -3,6 +3,7 @@ import { parseToAST } from "./markdown/parser.js";
|
|
|
3
3
|
import { extractListItems, renderSectionContent } from "./markdown/render.js";
|
|
4
4
|
import { normalizeSectionTitle } from "./markdown/titles.js";
|
|
5
5
|
import { markdownSourceSections, renderScopeFragment, validateScopeFragmentLines } from "./scope.js";
|
|
6
|
+
import { parseVerificationDeclarations, renderVerificationDeclarations, } from "./verification-declaration.js";
|
|
6
7
|
const KNOWN_SECTIONS = new Map([
|
|
7
8
|
["context", "context"],
|
|
8
9
|
["objective", "objective"],
|
|
@@ -82,6 +83,9 @@ function extractSingleLineDeclarations(section, heading) {
|
|
|
82
83
|
return trimmed;
|
|
83
84
|
});
|
|
84
85
|
}
|
|
86
|
+
function verificationDeclarations(section, heading) {
|
|
87
|
+
return parseVerificationDeclarations(renderSectionContent(section), { heading });
|
|
88
|
+
}
|
|
85
89
|
function collectBindDraftNodes(content) {
|
|
86
90
|
// Scope is parsed from source lines below. Hide its body from the Markdown AST
|
|
87
91
|
// so fence-like and heading-like payload bytes cannot acquire structure there.
|
|
@@ -183,7 +187,7 @@ export function parsePartialBindDraft(content) {
|
|
|
183
187
|
partial.criteria = extractSingleLineDeclarations(criteria, "Criteria");
|
|
184
188
|
const verification = sections.get("verification");
|
|
185
189
|
if (verification)
|
|
186
|
-
partial.verification =
|
|
190
|
+
partial.verification = verificationDeclarations(verification, "## Verification");
|
|
187
191
|
if (extensions.length > 0)
|
|
188
192
|
partial.extensions = extensions;
|
|
189
193
|
return partial;
|
|
@@ -254,7 +258,7 @@ export function renderMissingBindSectionsTemplate(missing) {
|
|
|
254
258
|
lines.push("## Criteria", "- <declarative acceptance criterion>");
|
|
255
259
|
continue;
|
|
256
260
|
}
|
|
257
|
-
lines.push("## Verification", "
|
|
261
|
+
lines.push("## Verification", "```bash", "<verification script>", "```");
|
|
258
262
|
}
|
|
259
263
|
return lines.join("\n");
|
|
260
264
|
}
|
|
@@ -331,7 +335,7 @@ export function parseBindDraft(content) {
|
|
|
331
335
|
design: designText,
|
|
332
336
|
scope: parsedScope,
|
|
333
337
|
criteria: extractSingleLineDeclarations(criteria, "Criteria"),
|
|
334
|
-
verification: verification ?
|
|
338
|
+
verification: verification ? verificationDeclarations(verification, "## Verification") : [],
|
|
335
339
|
extensions,
|
|
336
340
|
};
|
|
337
341
|
}
|
|
@@ -345,7 +349,7 @@ export function renderBindDraft(draft) {
|
|
|
345
349
|
`## Criteria\n${draft.criteria.map((criterion) => `- ${criterion}`).join("\n")}`,
|
|
346
350
|
];
|
|
347
351
|
if (draft.verification.length > 0) {
|
|
348
|
-
sections.push(`## Verification\n${draft.verification
|
|
352
|
+
sections.push(`## Verification\n${renderVerificationDeclarations(draft.verification)}`);
|
|
349
353
|
}
|
|
350
354
|
for (const extension of draft.extensions) {
|
|
351
355
|
const content = normalizeExtensionContent(extension.content);
|
|
@@ -391,6 +395,6 @@ export function compileBindDraftFlags(input) {
|
|
|
391
395
|
if (input.criteria !== undefined)
|
|
392
396
|
sections.push(`## Criteria\n- ${input.criteria}`);
|
|
393
397
|
if (input.verification !== undefined)
|
|
394
|
-
sections.push(`## Verification\n
|
|
398
|
+
sections.push(`## Verification\n\`\`\`bash\n${input.verification}\n\`\`\``);
|
|
395
399
|
return sections.length > 0 ? `${sections.join("\n\n")}\n` : "";
|
|
396
400
|
}
|
package/build/core/forfeit.js
CHANGED
|
@@ -88,7 +88,7 @@ export async function forfeitContract(input) {
|
|
|
88
88
|
: await context.publishAuthoritative({
|
|
89
89
|
verb: "forfeit",
|
|
90
90
|
admittedLedgerHead: admission.ledgerHead,
|
|
91
|
-
acceptedLifecycleTail: context.session.lifecycle.
|
|
91
|
+
acceptedLifecycleTail: context.session.lifecycle.contractHead,
|
|
92
92
|
entries: [prepared.entry],
|
|
93
93
|
delivery: {
|
|
94
94
|
ref: repositoryDeliveryRef(input.contractId),
|
|
@@ -33,3 +33,16 @@ export function effectiveCriteria(entries) {
|
|
|
33
33
|
}
|
|
34
34
|
return criteria;
|
|
35
35
|
}
|
|
36
|
+
/** Ordered effective contract Verification declarations from admitted lifecycle history. */
|
|
37
|
+
export function effectiveVerification(entries) {
|
|
38
|
+
const bind = entries.find((entry) => entry.kind === "bind");
|
|
39
|
+
if (!bind)
|
|
40
|
+
throw new FlowError("INTERNAL_STATE", "lifecycle history must contain bind entry");
|
|
41
|
+
let declarations = [...bind.data.verification];
|
|
42
|
+
for (const entry of entries) {
|
|
43
|
+
if (entry.kind === "amend" && entry.data.verificationDelta) {
|
|
44
|
+
declarations = [...entry.data.verificationDelta.replace];
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return declarations;
|
|
48
|
+
}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { withContractCarrierSession } from "./contract-carrier.js";
|
|
2
2
|
import { resolveAcceptedLifecycleTransaction, } from "./repository-ledger/accepted-tail.js";
|
|
3
|
+
import { lifecycleTransactionId } from "./repository-ledger/identity.js";
|
|
3
4
|
export async function reconcileCommittedMutation(plan) {
|
|
4
5
|
const observed = await withContractCarrierSession({ cwd: plan.cwd, contractId: plan.contractId, mode: "observe-only", signal: plan.signal }, async (session) => ({
|
|
5
6
|
model: session.lifecycle,
|
|
6
7
|
repository: session.repository,
|
|
7
8
|
}));
|
|
8
|
-
const authoritativeHead = observed.
|
|
9
|
-
|
|
9
|
+
const authoritativeHead = observed.repository.observedLedgerHead;
|
|
10
|
+
if (authoritativeHead === null) {
|
|
11
|
+
throw new Error(`accepted contract ${plan.contractId} has no repository ledger observation`);
|
|
12
|
+
}
|
|
13
|
+
const accepted = findLatestAcceptedTransaction(observed.repository, plan.contractId, observed.model.contractHead);
|
|
10
14
|
if (accepted.status === "refused") {
|
|
11
15
|
return {
|
|
12
16
|
kind: "refused",
|
|
@@ -38,6 +42,11 @@ export async function reconcileCommittedMutation(plan) {
|
|
|
38
42
|
return replayed;
|
|
39
43
|
return observeOrdinaryCoordinate(plan);
|
|
40
44
|
}
|
|
45
|
+
// A matching retry is still a mutating verb. Its accepted authority never
|
|
46
|
+
// proves that derived surfaces survived process death or manual removal.
|
|
47
|
+
const replayed = await alignDerivedTransaction(plan, transaction);
|
|
48
|
+
if (replayed.kind === "refused")
|
|
49
|
+
return replayed;
|
|
41
50
|
return {
|
|
42
51
|
kind: "aligned",
|
|
43
52
|
coordinate: { state: "verb-committed", invokedVerb: plan.mutation.verb, transaction, authoritativeHead },
|
|
@@ -50,23 +59,7 @@ export async function reconcileCommittedMutation(plan) {
|
|
|
50
59
|
refusal: { code: "CONTRACT_TERMINAL", message: `contract ${plan.contractId} is terminal (${observed.model.phase})` },
|
|
51
60
|
};
|
|
52
61
|
}
|
|
53
|
-
|
|
54
|
-
if (!bindObjectId) {
|
|
55
|
-
return {
|
|
56
|
-
kind: "refused",
|
|
57
|
-
authoritativeHead,
|
|
58
|
-
refusal: { code: "INTERNAL_STATE", message: `contract ${plan.contractId} typed lifecycle has no bind object` },
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
return {
|
|
62
|
-
kind: "aligned",
|
|
63
|
-
coordinate: {
|
|
64
|
-
state: "bind-aligned",
|
|
65
|
-
invokedVerb: plan.mutation.verb,
|
|
66
|
-
bindObjectId,
|
|
67
|
-
authoritativeHead,
|
|
68
|
-
},
|
|
69
|
-
};
|
|
62
|
+
return bindAligned(observed.repository, observed.model, plan, authoritativeHead);
|
|
70
63
|
}
|
|
71
64
|
async function alignDerivedTransaction(plan, transaction) {
|
|
72
65
|
return withContractCarrierSession({ cwd: plan.cwd, contractId: plan.contractId, mode: "mutation", signal: plan.signal }, async (session) => {
|
|
@@ -81,7 +74,7 @@ async function alignDerivedTransaction(plan, transaction) {
|
|
|
81
74
|
catch (error) {
|
|
82
75
|
return {
|
|
83
76
|
kind: "refused",
|
|
84
|
-
authoritativeHead: session.
|
|
77
|
+
authoritativeHead: session.pins.repositoryLedgerHead,
|
|
85
78
|
refusal: {
|
|
86
79
|
code: "LIFECYCLE_RECONCILIATION_INCOMPLETE",
|
|
87
80
|
message: error instanceof Error ? error.message : `contract ${plan.contractId} derived replay did not complete`,
|
|
@@ -91,18 +84,30 @@ async function alignDerivedTransaction(plan, transaction) {
|
|
|
91
84
|
});
|
|
92
85
|
}
|
|
93
86
|
async function observeOrdinaryCoordinate(plan) {
|
|
94
|
-
return withContractCarrierSession({ cwd: plan.cwd, contractId: plan.contractId, mode: "observe-only", signal: plan.signal }, async (session) => bindAligned(session.lifecycle
|
|
87
|
+
return withContractCarrierSession({ cwd: plan.cwd, contractId: plan.contractId, mode: "observe-only", signal: plan.signal }, async (session) => bindAligned(session.repository, session.lifecycle, plan, session.pins.repositoryLedgerHead));
|
|
95
88
|
}
|
|
96
|
-
function bindAligned(
|
|
97
|
-
const bindObjectId = history.find(({ entry }) => entry.kind === "bind")?.objectId;
|
|
98
|
-
|
|
89
|
+
function bindAligned(repository, lifecycle, plan, authoritativeHead) {
|
|
90
|
+
const bindObjectId = lifecycle.history.find(({ entry }) => entry.kind === "bind")?.objectId;
|
|
91
|
+
const transaction = repository.topology.nodes.find((node) => (node.type === "transaction"
|
|
92
|
+
&& node.contract === lifecycle.contract
|
|
93
|
+
&& node.finalEntryObjectId === lifecycle.contractHead));
|
|
94
|
+
if (!bindObjectId || !transaction || transaction.type !== "transaction") {
|
|
99
95
|
return {
|
|
100
96
|
kind: "refused",
|
|
101
97
|
authoritativeHead,
|
|
102
|
-
refusal: { code: "INTERNAL_STATE", message: "typed lifecycle
|
|
98
|
+
refusal: { code: "INTERNAL_STATE", message: "typed lifecycle lacks a replayable bind transaction" },
|
|
103
99
|
};
|
|
104
100
|
}
|
|
105
|
-
return {
|
|
101
|
+
return {
|
|
102
|
+
kind: "aligned",
|
|
103
|
+
coordinate: {
|
|
104
|
+
state: "bind-aligned",
|
|
105
|
+
invokedVerb: plan.mutation.verb,
|
|
106
|
+
bindObjectId,
|
|
107
|
+
transactionObjectId: lifecycleTransactionId(transaction.objectId),
|
|
108
|
+
authoritativeHead,
|
|
109
|
+
},
|
|
110
|
+
};
|
|
106
111
|
}
|
|
107
112
|
function findLatestAcceptedTransaction(read, contractId, finalEntryObjectId) {
|
|
108
113
|
if (finalEntryObjectId === null)
|
|
@@ -112,6 +117,6 @@ function findLatestAcceptedTransaction(read, contractId, finalEntryObjectId) {
|
|
|
112
117
|
&& candidate.finalEntryObjectId === finalEntryObjectId);
|
|
113
118
|
if (!node || node.type !== "transaction")
|
|
114
119
|
return { status: "none" };
|
|
115
|
-
const resolved = resolveAcceptedLifecycleTransaction({ read, transactionObjectId: node.objectId, expectedContract: contractId });
|
|
120
|
+
const resolved = resolveAcceptedLifecycleTransaction({ read, transactionObjectId: lifecycleTransactionId(node.objectId), expectedContract: contractId });
|
|
116
121
|
return resolved.status === "accepted" ? { status: "accepted", transaction: resolved } : resolved;
|
|
117
122
|
}
|
|
@@ -5,7 +5,7 @@ import { resolveAcceptedLifecycleTransaction } from "./repository-ledger/accepte
|
|
|
5
5
|
import { reconcileCommittedMutation, } from "./lifecycle-recovery.js";
|
|
6
6
|
function coordinateObject(pins, target) {
|
|
7
7
|
return {
|
|
8
|
-
ledgerHead: pins.
|
|
8
|
+
ledgerHead: pins.repositoryLedgerHead,
|
|
9
9
|
targetRef: pins.target.ref,
|
|
10
10
|
targetHead: pins.target.head,
|
|
11
11
|
deliveryRef: pins.delivery.ref,
|
|
@@ -20,7 +20,7 @@ function sameCoordinates(admission, session) {
|
|
|
20
20
|
}
|
|
21
21
|
function validateReplayCoordinate(session, coordinate) {
|
|
22
22
|
if (coordinate.state === "bind-aligned") {
|
|
23
|
-
const currentHead = session.
|
|
23
|
+
const currentHead = session.pins.repositoryLedgerHead;
|
|
24
24
|
if (coordinate.authoritativeHead !== currentHead) {
|
|
25
25
|
return {
|
|
26
26
|
code: "LIFECYCLE_REPLAY_COORDINATES_CHANGED",
|
|
@@ -55,7 +55,7 @@ function validateReplayCoordinate(session, coordinate) {
|
|
|
55
55
|
},
|
|
56
56
|
current: {
|
|
57
57
|
status: resolved.status,
|
|
58
|
-
authoritativeHead: session.
|
|
58
|
+
authoritativeHead: session.pins.repositoryLedgerHead,
|
|
59
59
|
},
|
|
60
60
|
};
|
|
61
61
|
}
|
|
@@ -115,12 +115,12 @@ export async function runLifecycle(plan) {
|
|
|
115
115
|
};
|
|
116
116
|
}
|
|
117
117
|
const attempt = await withContractCarrierSession({ cwd: plan.cwd, contractId: plan.contractId, mode: "observe-only", signal: plan.signal }, async (session) => {
|
|
118
|
-
observedHead = session.
|
|
118
|
+
observedHead = session.pins.repositoryLedgerHead;
|
|
119
119
|
const replayRefusal = validateReplayCoordinate(session, reconciliation.coordinate);
|
|
120
120
|
if (replayRefusal) {
|
|
121
121
|
return { kind: "refused", outcome: refusedLifecycleFacts({
|
|
122
122
|
contractId: plan.contractId,
|
|
123
|
-
authoritativeHead: session.
|
|
123
|
+
authoritativeHead: session.pins.repositoryLedgerHead,
|
|
124
124
|
...surfaceCoordinateRefusal(plan.mutation, replayRefusal),
|
|
125
125
|
}) };
|
|
126
126
|
}
|
|
@@ -130,13 +130,13 @@ export async function runLifecycle(plan) {
|
|
|
130
130
|
}
|
|
131
131
|
catch (error) {
|
|
132
132
|
if (error instanceof FlowError && !isCancellation(error)) {
|
|
133
|
-
return { kind: "refused", outcome: refusedLifecycleFacts({ contractId: plan.contractId, authoritativeHead: session.
|
|
133
|
+
return { kind: "refused", outcome: refusedLifecycleFacts({ contractId: plan.contractId, authoritativeHead: session.pins.repositoryLedgerHead, ...flowRefusal(error) }) };
|
|
134
134
|
}
|
|
135
135
|
throw error;
|
|
136
136
|
}
|
|
137
137
|
return {
|
|
138
138
|
kind: "admitted",
|
|
139
|
-
ledgerHead: session.
|
|
139
|
+
ledgerHead: session.pins.repositoryLedgerHead,
|
|
140
140
|
lifecycle: session.lifecycle,
|
|
141
141
|
repository: session.repository,
|
|
142
142
|
pins: session.pins,
|
|
@@ -172,20 +172,20 @@ export async function runLifecycle(plan) {
|
|
|
172
172
|
throw error;
|
|
173
173
|
}
|
|
174
174
|
try {
|
|
175
|
-
return await withContractCarrierSession({ cwd: plan.cwd, contractId: plan.contractId, mode: "mutation", signal: plan.signal, ...(plan.renewDeliveryProjector ? { renewDeliveryProjector: plan.renewDeliveryProjector } : {}), ...(plan.terminalWorkspaceCleanup ? { terminalWorkspaceCleanup: plan.terminalWorkspaceCleanup } : {}) }, async (session) => {
|
|
176
|
-
observedHead = session.
|
|
175
|
+
return await withContractCarrierSession({ cwd: plan.cwd, contractId: plan.contractId, mode: "mutation", signal: plan.signal, ...(plan.renewDeliveryProjector ? { renewDeliveryProjector: plan.renewDeliveryProjector } : {}), ...(plan.terminalWorkspaceCleanup ? { terminalWorkspaceCleanup: plan.terminalWorkspaceCleanup } : {}), ...(plan.transactionRunner ? { transactionRunner: plan.transactionRunner } : {}) }, async (session) => {
|
|
176
|
+
observedHead = session.pins.repositoryLedgerHead;
|
|
177
177
|
const replayRefusal = validateReplayCoordinate(session, admission.replay);
|
|
178
178
|
if (replayRefusal) {
|
|
179
179
|
return { kind: "noncommitted", outcome: refusedLifecycleFacts({
|
|
180
180
|
contractId: plan.contractId,
|
|
181
|
-
authoritativeHead: session.
|
|
181
|
+
authoritativeHead: session.pins.repositoryLedgerHead,
|
|
182
182
|
...surfaceCoordinateRefusal(plan.mutation, replayRefusal),
|
|
183
183
|
}) };
|
|
184
184
|
}
|
|
185
185
|
if (!sameCoordinates(admission, session)) {
|
|
186
186
|
return { kind: "noncommitted", outcome: refusedLifecycleFacts({
|
|
187
187
|
contractId: plan.contractId,
|
|
188
|
-
authoritativeHead: session.
|
|
188
|
+
authoritativeHead: session.pins.repositoryLedgerHead,
|
|
189
189
|
code: "LIFECYCLE_COORDINATES_CHANGED",
|
|
190
190
|
message: "contract coordinates changed during lifecycle preparation",
|
|
191
191
|
pinned: coordinateObject(admission.pins, admission.target),
|
|
@@ -202,7 +202,7 @@ export async function runLifecycle(plan) {
|
|
|
202
202
|
refusal = flowRefusal(error);
|
|
203
203
|
}
|
|
204
204
|
if (refusal) {
|
|
205
|
-
return { kind: "noncommitted", outcome: refusedLifecycleFacts({ contractId: plan.contractId, authoritativeHead: session.
|
|
205
|
+
return { kind: "noncommitted", outcome: refusedLifecycleFacts({ contractId: plan.contractId, authoritativeHead: session.pins.repositoryLedgerHead, ...refusal }) };
|
|
206
206
|
}
|
|
207
207
|
const effectSession = Object.freeze({
|
|
208
208
|
stableRoot: session.stableRoot,
|
|
@@ -239,7 +239,7 @@ export async function runLifecycle(plan) {
|
|
|
239
239
|
if (error instanceof FlowError && !isCancellation(error)) {
|
|
240
240
|
return { kind: "noncommitted", outcome: refusedLifecycleFacts({
|
|
241
241
|
contractId: plan.contractId,
|
|
242
|
-
authoritativeHead: session.
|
|
242
|
+
authoritativeHead: session.pins.repositoryLedgerHead,
|
|
243
243
|
...flowRefusal(error),
|
|
244
244
|
}) };
|
|
245
245
|
}
|
|
@@ -274,7 +274,7 @@ export async function runLifecycle(plan) {
|
|
|
274
274
|
kind: "noncommitted",
|
|
275
275
|
outcome: precommitLifecycleFacts({
|
|
276
276
|
contractId: plan.contractId,
|
|
277
|
-
authoritativeHead: session.
|
|
277
|
+
authoritativeHead: session.pins.repositoryLedgerHead,
|
|
278
278
|
journal: session.journal,
|
|
279
279
|
cancellationObserved: session.cancellationObserved(),
|
|
280
280
|
}),
|
package/build/core/log.js
CHANGED
|
@@ -44,7 +44,7 @@ export async function readContractLog(input) {
|
|
|
44
44
|
at: entry.at,
|
|
45
45
|
actor: entry.actor,
|
|
46
46
|
})),
|
|
47
|
-
markdown: renderCurrentContractView(model),
|
|
47
|
+
markdown: renderCurrentContractView(model, read.observedLedgerHead),
|
|
48
48
|
...(warning ? { amendmentWarning: warning } : {}),
|
|
49
49
|
};
|
|
50
50
|
}
|
|
@@ -88,14 +88,14 @@ export class EffectJournal {
|
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
export function carrierLifecycleEffect(carrier) {
|
|
91
|
-
const idempotencyKey = `carrier:${carrier.
|
|
91
|
+
const idempotencyKey = `carrier:${carrier.contractHead}`;
|
|
92
92
|
if (carrier.status === "failed") {
|
|
93
93
|
return { effect: "carrier-projection", idempotencyKey, status: "failed", cause: { code: "CARRIER_MATERIALIZATION_FAILED", message: carrier.cause } };
|
|
94
94
|
}
|
|
95
95
|
if (carrier.status === "target-unavailable") {
|
|
96
96
|
return { effect: "carrier-projection", idempotencyKey, status: "failed", cause: { code: "CARRIER_TARGET_UNAVAILABLE", message: carrier.reason } };
|
|
97
97
|
}
|
|
98
|
-
return { effect: "carrier-projection", idempotencyKey, status: "succeeded", receipt: {
|
|
98
|
+
return { effect: "carrier-projection", idempotencyKey, status: "succeeded", receipt: { contractHead: carrier.contractHead, status: carrier.status } };
|
|
99
99
|
}
|
|
100
100
|
export function committedLifecycleFacts(input) {
|
|
101
101
|
return {
|