@astrosheep/keiyaku 2.9.11 → 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 +79 -85
- package/build/.tsbuildinfo +1 -1
- package/build/agents/call-terms.js +1 -0
- package/build/agents/providers/claude-agent-sdk/adapter.js +17 -4
- 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/bind/meta.js +4 -4
- 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 +14 -10
- 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/handler.js +4 -5
- 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 +2 -2
- 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 +4 -0
- package/build/cli/commands/task/compose/handler.js +18 -0
- package/build/cli/commands/task/compose/meta.js +10 -0
- 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 +48 -12
- package/build/cli/help.js +29 -30
- package/build/cli/index.js +14 -8
- package/build/cli/parse-flags.js +60 -21
- package/build/cli/parse-metadata.js +10 -4
- package/build/cli/parse-selectors.js +22 -4
- package/build/cli/parse.js +43 -30
- package/build/cli/projection-address.js +7 -7
- 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 +33 -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/addressing.js +3 -3
- 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 +46 -33
- 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 +43 -6
- package/build/core/contract.js +2 -1
- package/build/core/derived-replay.js +168 -35
- package/build/core/draft.js +60 -27
- 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 +19 -14
- 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 +132 -76
- 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 +24 -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 +8 -8
- package/build/core/projection/projection-kill.js +20 -10
- 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 +48 -29
- package/build/core/projection/projection-wake.js +28 -88
- 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/claim-evidence.js +1 -1
- package/build/core/repository-ledger/codec.js +11 -5
- package/build/core/repository-ledger/current-state-store.js +464 -349
- 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 +57 -9
- package/build/core/repository-ledger/write-transaction.js +24 -17
- 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/run-control/sqlite-process-lifetime-lock.js +174 -0
- 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 +328 -0
- package/build/core/task/document.js +102 -39
- package/build/core/task/index.js +4 -3
- package/build/core/task/settlement-git.js +70 -30
- 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-git-store.js +1 -0
- package/build/core/task/task-store-repository.js +2 -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 +15 -2
- 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 +2 -2
- package/skills/keiyaku-akuma/SKILL.md +27 -21
- package/skills/keiyaku-task/SKILL.md +61 -5
- package/skills/keiyaku-workflow/SKILL.md +81 -26
- 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 -382
- 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,8 +3,16 @@ 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
|
-
|
|
7
|
-
const
|
|
6
|
+
import { parseVerificationDeclarations, renderVerificationDeclarations, } from "./verification-declaration.js";
|
|
7
|
+
const KNOWN_SECTIONS = new Map([
|
|
8
|
+
["context", "context"],
|
|
9
|
+
["objective", "objective"],
|
|
10
|
+
["design & approach", "design"],
|
|
11
|
+
["scope", "scope"],
|
|
12
|
+
["criteria", "criteria"],
|
|
13
|
+
["verification", "verification"],
|
|
14
|
+
]);
|
|
15
|
+
const RETIRED_OR_MALFORMED_CORE_SECTIONS = new Set(["title", "name", "design", "checks"]);
|
|
8
16
|
const BIND_DRAFT_SECTION_ORDER = ["title", "context", "objective", "design", "scope", "criteria", "verification"];
|
|
9
17
|
function invalidBindDraft(message) {
|
|
10
18
|
throw new FlowError("INVALID_KEIYAKU_DRAFT", `INVALID_BIND_DRAFT: ${message}`);
|
|
@@ -75,6 +83,9 @@ function extractSingleLineDeclarations(section, heading) {
|
|
|
75
83
|
return trimmed;
|
|
76
84
|
});
|
|
77
85
|
}
|
|
86
|
+
function verificationDeclarations(section, heading) {
|
|
87
|
+
return parseVerificationDeclarations(renderSectionContent(section), { heading });
|
|
88
|
+
}
|
|
78
89
|
function collectBindDraftNodes(content) {
|
|
79
90
|
// Scope is parsed from source lines below. Hide its body from the Markdown AST
|
|
80
91
|
// so fence-like and heading-like payload bytes cannot acquire structure there.
|
|
@@ -105,14 +116,19 @@ function collectBindDraftNodes(content) {
|
|
|
105
116
|
continue;
|
|
106
117
|
const normalized = normalizeSectionName(node.title);
|
|
107
118
|
if (RETIRED_OR_MALFORMED_CORE_SECTIONS.has(normalized)) {
|
|
108
|
-
const replacement = normalized === "checks"
|
|
119
|
+
const replacement = normalized === "checks"
|
|
120
|
+
? "Criteria and Verification"
|
|
121
|
+
: normalized === "design"
|
|
122
|
+
? "Design & Approach"
|
|
123
|
+
: "the H1 Title";
|
|
109
124
|
invalidBindDraft(`obsolete or malformed core section '${node.title}'; use ${replacement}`);
|
|
110
125
|
}
|
|
111
|
-
|
|
112
|
-
|
|
126
|
+
const coreSection = KNOWN_SECTIONS.get(normalized);
|
|
127
|
+
if (coreSection) {
|
|
128
|
+
if (sections.has(coreSection)) {
|
|
113
129
|
invalidBindDraft(`duplicate section '${node.title}' in bind draft`);
|
|
114
130
|
}
|
|
115
|
-
sections.set(
|
|
131
|
+
sections.set(coreSection, node);
|
|
116
132
|
continue;
|
|
117
133
|
}
|
|
118
134
|
extensions.push({
|
|
@@ -155,7 +171,9 @@ export function parsePartialBindDraft(content) {
|
|
|
155
171
|
if (!section)
|
|
156
172
|
continue;
|
|
157
173
|
const text = renderSectionContent(section).trim();
|
|
158
|
-
const heading =
|
|
174
|
+
const heading = sectionName === "design"
|
|
175
|
+
? "Design & Approach"
|
|
176
|
+
: `${sectionName[0].toUpperCase()}${sectionName.slice(1)}`;
|
|
159
177
|
if (!text)
|
|
160
178
|
invalidBindDraft(`${heading} section cannot be empty`);
|
|
161
179
|
partial[sectionName] = text;
|
|
@@ -169,7 +187,7 @@ export function parsePartialBindDraft(content) {
|
|
|
169
187
|
partial.criteria = extractSingleLineDeclarations(criteria, "Criteria");
|
|
170
188
|
const verification = sections.get("verification");
|
|
171
189
|
if (verification)
|
|
172
|
-
partial.verification =
|
|
190
|
+
partial.verification = verificationDeclarations(verification, "## Verification");
|
|
173
191
|
if (extensions.length > 0)
|
|
174
192
|
partial.extensions = extensions;
|
|
175
193
|
return partial;
|
|
@@ -229,7 +247,7 @@ export function renderMissingBindSectionsTemplate(missing) {
|
|
|
229
247
|
continue;
|
|
230
248
|
}
|
|
231
249
|
if (section === "design") {
|
|
232
|
-
lines.push("## Design", "<settled owner, boundaries, flow, state, and failure behavior>");
|
|
250
|
+
lines.push("## Design & Approach", "<settled owner, boundaries, flow, state, and failure behavior>");
|
|
233
251
|
continue;
|
|
234
252
|
}
|
|
235
253
|
if (section === "scope") {
|
|
@@ -240,18 +258,28 @@ export function renderMissingBindSectionsTemplate(missing) {
|
|
|
240
258
|
lines.push("## Criteria", "- <declarative acceptance criterion>");
|
|
241
259
|
continue;
|
|
242
260
|
}
|
|
243
|
-
lines.push("## Verification", "
|
|
261
|
+
lines.push("## Verification", "```bash", "<verification script>", "```");
|
|
244
262
|
}
|
|
245
263
|
return lines.join("\n");
|
|
246
264
|
}
|
|
247
265
|
export function describeMissingBindSections(missing) {
|
|
248
|
-
|
|
266
|
+
const labels = {
|
|
267
|
+
title: "Title",
|
|
268
|
+
context: "Context",
|
|
269
|
+
objective: "Objective",
|
|
270
|
+
design: "Design & Approach",
|
|
271
|
+
scope: "Scope",
|
|
272
|
+
criteria: "Criteria",
|
|
273
|
+
verification: "Verification",
|
|
274
|
+
};
|
|
275
|
+
const named = missing.map((section) => labels[section]);
|
|
276
|
+
if (named.length === 0)
|
|
249
277
|
return "";
|
|
250
|
-
if (
|
|
251
|
-
return
|
|
252
|
-
if (
|
|
253
|
-
return `${
|
|
254
|
-
return `${
|
|
278
|
+
if (named.length === 1)
|
|
279
|
+
return named[0];
|
|
280
|
+
if (named.length === 2)
|
|
281
|
+
return `${named[0]} or ${named[1]}`;
|
|
282
|
+
return `${named.slice(0, -1).join(", ")}, or ${named[named.length - 1]}`;
|
|
255
283
|
}
|
|
256
284
|
export function materializeBindDraft(partial) {
|
|
257
285
|
const missing = missingBindDraftSections(partial);
|
|
@@ -272,7 +300,7 @@ export function materializeBindDraft(partial) {
|
|
|
272
300
|
export function parseBindDraft(content) {
|
|
273
301
|
const { title, sections, extensions } = collectBindDraftNodes(content);
|
|
274
302
|
if (!title) {
|
|
275
|
-
invalidBindDraft("missing H1 Title in bind draft; expected '# <title>' followed by Context, Objective, Design, Scope, and Criteria; Verification is optional");
|
|
303
|
+
invalidBindDraft("missing H1 Title in bind draft; expected '# <title>' followed by Context, Objective, Design & Approach, Scope, and Criteria; Verification is optional");
|
|
276
304
|
}
|
|
277
305
|
const context = sections.get("context");
|
|
278
306
|
const objective = sections.get("objective");
|
|
@@ -285,7 +313,7 @@ export function parseBindDraft(content) {
|
|
|
285
313
|
if (!objective)
|
|
286
314
|
invalidBindDraft("missing Objective section in bind draft");
|
|
287
315
|
if (!design)
|
|
288
|
-
invalidBindDraft("missing Design section in bind draft");
|
|
316
|
+
invalidBindDraft("missing Design & Approach section in bind draft");
|
|
289
317
|
if (!scope)
|
|
290
318
|
invalidBindDraft("missing Scope section in bind draft");
|
|
291
319
|
const parsedScope = parseScopeSection(content);
|
|
@@ -299,7 +327,7 @@ export function parseBindDraft(content) {
|
|
|
299
327
|
if (!objectiveText)
|
|
300
328
|
invalidBindDraft("Objective section cannot be empty");
|
|
301
329
|
if (!designText)
|
|
302
|
-
invalidBindDraft("Design section cannot be empty");
|
|
330
|
+
invalidBindDraft("Design & Approach section cannot be empty");
|
|
303
331
|
return {
|
|
304
332
|
title,
|
|
305
333
|
context: contextText,
|
|
@@ -307,7 +335,7 @@ export function parseBindDraft(content) {
|
|
|
307
335
|
design: designText,
|
|
308
336
|
scope: parsedScope,
|
|
309
337
|
criteria: extractSingleLineDeclarations(criteria, "Criteria"),
|
|
310
|
-
verification: verification ?
|
|
338
|
+
verification: verification ? verificationDeclarations(verification, "## Verification") : [],
|
|
311
339
|
extensions,
|
|
312
340
|
};
|
|
313
341
|
}
|
|
@@ -316,12 +344,12 @@ export function renderBindDraft(draft) {
|
|
|
316
344
|
`# ${draft.title}`,
|
|
317
345
|
`## Context\n${draft.context}`,
|
|
318
346
|
`## Objective\n${draft.objective}`,
|
|
319
|
-
`## Design\n${draft.design}`,
|
|
347
|
+
`## Design & Approach\n${draft.design}`,
|
|
320
348
|
`## Scope\n${renderScopeFragment(draft.scope)}`,
|
|
321
349
|
`## Criteria\n${draft.criteria.map((criterion) => `- ${criterion}`).join("\n")}`,
|
|
322
350
|
];
|
|
323
351
|
if (draft.verification.length > 0) {
|
|
324
|
-
sections.push(`## Verification\n${draft.verification
|
|
352
|
+
sections.push(`## Verification\n${renderVerificationDeclarations(draft.verification)}`);
|
|
325
353
|
}
|
|
326
354
|
for (const extension of draft.extensions) {
|
|
327
355
|
const content = normalizeExtensionContent(extension.content);
|
|
@@ -340,7 +368,6 @@ export function compileBindDraftFlags(input) {
|
|
|
340
368
|
["--context", input.context],
|
|
341
369
|
["--objective", input.objective],
|
|
342
370
|
["--design", input.design],
|
|
343
|
-
["--scope", input.scope],
|
|
344
371
|
["--criteria", input.criteria],
|
|
345
372
|
["--verification", input.verification],
|
|
346
373
|
]) {
|
|
@@ -348,6 +375,12 @@ export function compileBindDraftFlags(input) {
|
|
|
348
375
|
throw new FlowError("EMPTY_PARAM", `${flag} must be a single line`);
|
|
349
376
|
}
|
|
350
377
|
}
|
|
378
|
+
const scope = typeof input.scope === "string" ? [input.scope] : input.scope;
|
|
379
|
+
for (const pattern of scope ?? []) {
|
|
380
|
+
if (pattern.includes("\n") || pattern.includes("\r")) {
|
|
381
|
+
throw new FlowError("EMPTY_PARAM", "--scope must be a single line");
|
|
382
|
+
}
|
|
383
|
+
}
|
|
351
384
|
const sections = [];
|
|
352
385
|
if (input.title !== undefined)
|
|
353
386
|
sections.push(`# ${input.title}`);
|
|
@@ -356,12 +389,12 @@ export function compileBindDraftFlags(input) {
|
|
|
356
389
|
if (input.objective !== undefined)
|
|
357
390
|
sections.push(`## Objective\n${input.objective}`);
|
|
358
391
|
if (input.design !== undefined)
|
|
359
|
-
sections.push(`## Design\n${input.design}`);
|
|
360
|
-
if (
|
|
361
|
-
sections.push(`## Scope\n~~~\n${
|
|
392
|
+
sections.push(`## Design & Approach\n${input.design}`);
|
|
393
|
+
if (scope !== undefined)
|
|
394
|
+
sections.push(`## Scope\n~~~\n${scope.join("\n")}\n~~~`);
|
|
362
395
|
if (input.criteria !== undefined)
|
|
363
396
|
sections.push(`## Criteria\n- ${input.criteria}`);
|
|
364
397
|
if (input.verification !== undefined)
|
|
365
|
-
sections.push(`## Verification\n
|
|
398
|
+
sections.push(`## Verification\n\`\`\`bash\n${input.verification}\n\`\`\``);
|
|
366
399
|
return sections.length > 0 ? `${sections.join("\n\n")}\n` : "";
|
|
367
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
|
}
|