@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
package/build/core/renew.js
CHANGED
|
@@ -6,6 +6,7 @@ import { withContractCarrierSession } from "./contract-carrier.js";
|
|
|
6
6
|
import { runLifecycle } from "./lifecycle-runner.js";
|
|
7
7
|
import { committedLifecycleFacts, derivedCleanupFacts, isCompleteLifecycleOutcome, } from "./outcome-base.js";
|
|
8
8
|
import { abortDetachedRenewChain, readDetachedRenewScratch, renewScratchName, } from "./renew-rewrite.js";
|
|
9
|
+
import { scopePathEvidence } from "./scope.js";
|
|
9
10
|
import { persistResponseHistory } from "./transcripts.js";
|
|
10
11
|
import { admitRenew, checkCancellation, prepareRenew, sameAdmission, scopeEntry } from "./renew-plan.js";
|
|
11
12
|
async function persistRenewResponse(journal, facts, persist) {
|
|
@@ -46,6 +47,7 @@ async function executeRenew(input, continuation) {
|
|
|
46
47
|
},
|
|
47
48
|
signal: input.signal,
|
|
48
49
|
...(input.renewDeliveryProjector ? { renewDeliveryProjector: input.renewDeliveryProjector } : {}),
|
|
50
|
+
...(input.transactionRunner ? { transactionRunner: input.transactionRunner } : {}),
|
|
49
51
|
admit: (session, replay) => admitRenew(session, input, replay, continuation?.scratch),
|
|
50
52
|
prepare: (admission) => prepareRenew(admission.business, input, continuation),
|
|
51
53
|
revalidate: async ({ session, admission }) => {
|
|
@@ -79,7 +81,11 @@ async function executeRenew(input, continuation) {
|
|
|
79
81
|
if (admission.business.acceptedReplay) {
|
|
80
82
|
const accepted = admission.business.acceptedReplay;
|
|
81
83
|
const replayed = await context.replayCommitted({ transactionObjectId: accepted.transactionObjectId });
|
|
82
|
-
const sealed = {
|
|
84
|
+
const sealed = {
|
|
85
|
+
...accepted.seal.data,
|
|
86
|
+
scopeViolationEvidence: await scopePathEvidence(admission.business.targetRoot, admission.business.model.effectiveScope, accepted.seal.data.scopeAudit.violations),
|
|
87
|
+
ledgerHead: accepted.sealObjectId,
|
|
88
|
+
};
|
|
83
89
|
const value = {
|
|
84
90
|
...committedLifecycleFacts({ head: accepted.transactionObjectId, entryKind: "renew", contractId: input.contractId, carrier: replayed.carrier }),
|
|
85
91
|
contractId: input.contractId,
|
|
@@ -95,13 +101,6 @@ async function executeRenew(input, continuation) {
|
|
|
95
101
|
carrier: replayed.carrier,
|
|
96
102
|
targetRoot: admission.business.targetRoot,
|
|
97
103
|
};
|
|
98
|
-
if (continuation) {
|
|
99
|
-
await context.journal.run({
|
|
100
|
-
effect: "renew-scratch-cleanup",
|
|
101
|
-
idempotencyKey: `${input.contractId}:${accepted.transactionObjectId}:renew-scratch-cleanup`,
|
|
102
|
-
run: async () => abortDetachedRenewChain({ cwd: input.cwd, scratchName: continuation.scratch.scratchName }),
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
104
|
await persistRenewResponse(context.journal, value, input.persistResponse);
|
|
106
105
|
return { kind: "committed", commit: replayed, entryKind: "renew", value };
|
|
107
106
|
}
|
|
@@ -140,7 +139,7 @@ async function executeRenew(input, continuation) {
|
|
|
140
139
|
if (!admission.business.amendment) {
|
|
141
140
|
const transactionObjectId = admission.replay.state === "verb-committed"
|
|
142
141
|
? admission.replay.transaction.transactionObjectId
|
|
143
|
-
: admission.
|
|
142
|
+
: admission.replay.transactionObjectId;
|
|
144
143
|
const replayed = await context.replayCommitted({ transactionObjectId });
|
|
145
144
|
const accepted = replayed.transaction.entries.at(-1);
|
|
146
145
|
if (!accepted)
|
|
@@ -166,7 +165,7 @@ async function executeRenew(input, continuation) {
|
|
|
166
165
|
const published = await context.publishAuthoritative({
|
|
167
166
|
verb: "renew",
|
|
168
167
|
admittedLedgerHead: admission.ledgerHead,
|
|
169
|
-
acceptedLifecycleTail: context.session.lifecycle.
|
|
168
|
+
acceptedLifecycleTail: context.session.lifecycle.contractHead,
|
|
170
169
|
entries: [admission.business.amendment],
|
|
171
170
|
});
|
|
172
171
|
if (published.status === "refused-before-effects")
|
|
@@ -203,15 +202,19 @@ async function executeRenew(input, continuation) {
|
|
|
203
202
|
const published = await context.publishAuthoritative({
|
|
204
203
|
verb: "renew",
|
|
205
204
|
admittedLedgerHead: admission.ledgerHead,
|
|
206
|
-
acceptedLifecycleTail: context.session.lifecycle.
|
|
205
|
+
acceptedLifecycleTail: context.session.lifecycle.contractHead,
|
|
207
206
|
entries,
|
|
207
|
+
target: {
|
|
208
|
+
ref: admission.business.targetRef,
|
|
209
|
+
expectedOid: prepared.entry.data.newBase,
|
|
210
|
+
},
|
|
208
211
|
delivery: {
|
|
209
212
|
ref: admission.business.deliveryRef,
|
|
210
213
|
expectedOid: prepared.entry.data.oldHead,
|
|
211
214
|
},
|
|
212
215
|
});
|
|
213
216
|
if (published.status === "refused-before-effects")
|
|
214
|
-
return { kind: "refused-before-effects", authoritativeHead: admission.ledgerHead, refusal: { code: "CONCURRENT_MODIFICATION", message: "repository ledger or delivery ref changed before renew transaction" } };
|
|
217
|
+
return { kind: "refused-before-effects", authoritativeHead: admission.ledgerHead, refusal: { code: "CONCURRENT_MODIFICATION", message: "repository target, ledger, or delivery ref changed before renew transaction" } };
|
|
215
218
|
if (published.status === "indeterminate")
|
|
216
219
|
return { kind: "precommit-effects-only", authoritativeHead: admission.ledgerHead };
|
|
217
220
|
const sealIndex = published.transaction.entries.findIndex((entry) => entry.kind === "seal");
|
|
@@ -239,13 +242,6 @@ async function executeRenew(input, continuation) {
|
|
|
239
242
|
carrier: published.carrier,
|
|
240
243
|
targetRoot: admission.business.targetRoot,
|
|
241
244
|
};
|
|
242
|
-
if (continuation) {
|
|
243
|
-
await context.journal.run({
|
|
244
|
-
effect: "renew-scratch-cleanup",
|
|
245
|
-
idempotencyKey: `${input.contractId}:${published.head}:renew-scratch-cleanup`,
|
|
246
|
-
run: async () => abortDetachedRenewChain({ cwd: input.cwd, scratchName: continuation.scratch.scratchName }),
|
|
247
|
-
});
|
|
248
|
-
}
|
|
249
245
|
await persistRenewResponse(context.journal, value, input.persistResponse);
|
|
250
246
|
return { kind: "committed", commit: published, entryKind: "renew", value };
|
|
251
247
|
},
|
|
@@ -261,19 +257,7 @@ export async function renewContract(input) {
|
|
|
261
257
|
}
|
|
262
258
|
export async function continueRenewContract(input) {
|
|
263
259
|
const scratchName = renewScratchName(input.contractId);
|
|
264
|
-
let
|
|
265
|
-
try {
|
|
266
|
-
scratch = await readDetachedRenewScratch({ cwd: input.cwd, scratchName });
|
|
267
|
-
}
|
|
268
|
-
catch (error) {
|
|
269
|
-
throw new FlowError("RENEW_SCRATCH_NOT_FOUND", `no renew conflict scratch is open for ${input.contractId}`, { cause: error });
|
|
270
|
-
}
|
|
271
|
-
return executeRenew(input, { scratch, resolution: input.resolution ?? { kind: "scratch-files" } });
|
|
272
|
-
}
|
|
273
|
-
export async function abortRenewContract(input) {
|
|
274
|
-
checkCancellation(input.signal);
|
|
275
|
-
return withContractCarrierSession({ cwd: input.cwd, contractId: input.contractId, mode: "observe-only", signal: input.signal }, async (session) => {
|
|
276
|
-
const scratchName = renewScratchName(input.contractId);
|
|
260
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
277
261
|
let scratch;
|
|
278
262
|
try {
|
|
279
263
|
scratch = await readDetachedRenewScratch({ cwd: input.cwd, scratchName });
|
|
@@ -281,17 +265,37 @@ export async function abortRenewContract(input) {
|
|
|
281
265
|
catch (error) {
|
|
282
266
|
throw new FlowError("RENEW_SCRATCH_NOT_FOUND", `no renew conflict scratch is open for ${input.contractId}`, { cause: error });
|
|
283
267
|
}
|
|
268
|
+
const result = await executeRenew(input, { scratch, resolution: input.resolution ?? { kind: "scratch-files" } });
|
|
269
|
+
const code = result.family === "refused-before-effects" ? result.failedInvariant.code : null;
|
|
270
|
+
if (attempt === 0 && (code === "LIFECYCLE_COORDINATES_CHANGED"
|
|
271
|
+
|| code === "LIFECYCLE_REPLAY_COORDINATES_CHANGED"
|
|
272
|
+
|| code === "RENEW_COORDINATES_CHANGED"
|
|
273
|
+
|| code === "CONCURRENT_MODIFICATION"))
|
|
274
|
+
continue;
|
|
275
|
+
if (isCompleteLifecycleOutcome(result)) {
|
|
276
|
+
await abortDetachedRenewChain({ cwd: input.cwd, scratchName: scratch.scratchName });
|
|
277
|
+
}
|
|
278
|
+
return result;
|
|
279
|
+
}
|
|
280
|
+
throw new FlowError("INTERNAL_STATE", "bounded renew continuation retry exhausted unexpectedly");
|
|
281
|
+
}
|
|
282
|
+
export async function abortRenewContract(input) {
|
|
283
|
+
checkCancellation(input.signal);
|
|
284
|
+
return withContractCarrierSession({ cwd: input.cwd, contractId: input.contractId, mode: "observe-only", signal: input.signal }, async (session) => {
|
|
285
|
+
const scratchName = renewScratchName(input.contractId);
|
|
284
286
|
checkCancellation(input.signal);
|
|
285
287
|
const cleanup = await abortDetachedRenewChain({ cwd: input.cwd, scratchName });
|
|
288
|
+
if (cleanup.status === "absent") {
|
|
289
|
+
throw new FlowError("RENEW_SCRATCH_NOT_FOUND", `no renew conflict scratch is open for ${input.contractId}`);
|
|
290
|
+
}
|
|
286
291
|
return {
|
|
287
292
|
...derivedCleanupFacts({
|
|
288
293
|
contractId: input.contractId,
|
|
289
|
-
authoritativeHead: session.
|
|
294
|
+
authoritativeHead: session.pins.repositoryLedgerHead,
|
|
290
295
|
effect: "renew-conflict-scratch-cleanup",
|
|
291
|
-
receipt: { status: cleanup.status, scratchName
|
|
296
|
+
receipt: { status: cleanup.status, scratchName },
|
|
292
297
|
}),
|
|
293
298
|
contractId: input.contractId,
|
|
294
|
-
oldHead: scratch.oldHead,
|
|
295
299
|
scratchName,
|
|
296
300
|
carrier: session.initialCarrier,
|
|
297
301
|
};
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import { resolveLocalBranchHead } from "../../git/refs.js";
|
|
2
2
|
import { foldRepositoryLedger } from "./fold-repository.js";
|
|
3
3
|
import { REPOSITORY_LEDGER_REF } from "./codec.js";
|
|
4
|
+
import { repositoryLedgerHead } from "./identity.js";
|
|
4
5
|
import { traverseRepositoryLedger } from "./traversal.js";
|
|
5
6
|
export async function observeRepositoryLedgerHead(cwd) {
|
|
6
|
-
|
|
7
|
+
const head = (await resolveLocalBranchHead(cwd, REPOSITORY_LEDGER_REF))?.head;
|
|
8
|
+
return head === undefined ? null : repositoryLedgerHead(head);
|
|
7
9
|
}
|
|
8
10
|
/** The shared current-head typed traversal and fold used by full and targeted reads. */
|
|
9
11
|
export async function readAcceptedRepositoryFold(cwd, options = {}) {
|
|
10
|
-
const
|
|
11
|
-
if (
|
|
12
|
+
const observed = await (options.observeHead ?? observeRepositoryLedgerHead)(cwd);
|
|
13
|
+
if (observed === null)
|
|
12
14
|
return { observedLedgerHead: null, traversed: null, folded: null };
|
|
15
|
+
const observedLedgerHead = repositoryLedgerHead(observed);
|
|
13
16
|
const traversed = await traverseRepositoryLedger(cwd, observedLedgerHead, options.signal, { historicalSemanticEvidence: false });
|
|
14
17
|
return {
|
|
15
18
|
observedLedgerHead,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { verificationDeclarationSchema } from "../verification-declaration.js";
|
|
2
3
|
export const REPOSITORY_LEDGER_REF = "refs/heads/keiyaku-ledger";
|
|
3
4
|
export const REPOSITORY_DELIVERY_REF_PREFIX = "refs/heads/keiyaku/";
|
|
4
5
|
export function repositoryDeliveryRef(contract) {
|
|
@@ -61,7 +62,7 @@ function buildLifecycleEntrySchema(format) {
|
|
|
61
62
|
design: nonblank,
|
|
62
63
|
scope: z.array(nonblank).min(1),
|
|
63
64
|
criteria: z.array(nonblank),
|
|
64
|
-
verification: z.array(
|
|
65
|
+
verification: z.array(verificationDeclarationSchema),
|
|
65
66
|
extensions: z.array(extension),
|
|
66
67
|
base: oid,
|
|
67
68
|
target: fullBranchRefSchema,
|
|
@@ -103,7 +104,7 @@ function buildLifecycleEntrySchema(format) {
|
|
|
103
104
|
}
|
|
104
105
|
});
|
|
105
106
|
return z.discriminatedUnion("kind", [
|
|
106
|
-
lifecycleEnvelope("bind",
|
|
107
|
+
lifecycleEnvelope("bind", 3, bindData),
|
|
107
108
|
lifecycleEnvelope("bootstrap", 1, bootstrapData),
|
|
108
109
|
lifecycleEnvelope("arc", 1, z.object({
|
|
109
110
|
arc: safePositiveInteger,
|
|
@@ -124,10 +125,15 @@ function buildLifecycleEntrySchema(format) {
|
|
|
124
125
|
}).strict(),
|
|
125
126
|
scopeAudit: z.object({ violations: z.array(nonblank) }).strict(),
|
|
126
127
|
}).strict()),
|
|
127
|
-
lifecycleEnvelope("amend",
|
|
128
|
-
amendment:
|
|
128
|
+
lifecycleEnvelope("amend", 2, z.object({
|
|
129
|
+
amendment: z.string(),
|
|
129
130
|
scopeDelta: z.object({ add: z.array(nonblank).min(1) }).strict().optional(),
|
|
130
|
-
|
|
131
|
+
verificationDelta: z.object({ replace: z.array(verificationDeclarationSchema) }).strict().optional(),
|
|
132
|
+
}).strict().superRefine((value, context) => {
|
|
133
|
+
if (!value.amendment.trim() && !value.scopeDelta && !value.verificationDelta) {
|
|
134
|
+
context.addIssue({ code: "custom", message: "amend requires prose, a scope delta, or a verification delta" });
|
|
135
|
+
}
|
|
136
|
+
})),
|
|
131
137
|
lifecycleEnvelope("renew", 1, z.object({
|
|
132
138
|
oldBase: oid,
|
|
133
139
|
newBase: oid,
|
|
@@ -3,10 +3,11 @@ import * as path from "node:path";
|
|
|
3
3
|
import { createHash, randomUUID } from "node:crypto";
|
|
4
4
|
import { DatabaseSync } from "node:sqlite";
|
|
5
5
|
import { runGitProcess } from "../../git/core.js";
|
|
6
|
-
import { acquireSqliteProcessLifetimeLock } from "../sqlite-process-lifetime-lock.js";
|
|
6
|
+
import { acquireSqliteProcessLifetimeLock } from "../run-control/sqlite-process-lifetime-lock.js";
|
|
7
7
|
import { applyFoldEntry } from "./fold.js";
|
|
8
8
|
import { createLifecycleEntrySchema, evidenceObjectId, repositoryDeliveryRef } from "./codec.js";
|
|
9
|
-
|
|
9
|
+
import { repositoryLedgerHead } from "./identity.js";
|
|
10
|
+
export const REPOSITORY_CURRENT_STATE_VERSION = 4;
|
|
10
11
|
export const REPOSITORY_CURRENT_STATE_PREFIX = "keiyaku-current-state-";
|
|
11
12
|
export const REPOSITORY_CURRENT_STATE_SUFFIX = ".sqlite";
|
|
12
13
|
const SNAPSHOT_READER_BUSY_MS = 5_000;
|
|
@@ -67,7 +68,7 @@ function exactRows(database, sql, ...params) {
|
|
|
67
68
|
return database.prepare(sql).all(...params);
|
|
68
69
|
}
|
|
69
70
|
function pinDigest(pin) {
|
|
70
|
-
return digest(["keiyaku-current-state-
|
|
71
|
+
return digest(["keiyaku-current-state-v4", REPOSITORY_CURRENT_STATE_VERSION, pin.head, pin.objectFormat, pin.emptyTree]);
|
|
71
72
|
}
|
|
72
73
|
function repositoryDigest(database) {
|
|
73
74
|
return digest(["repository", exactRows(database, "SELECT singleton, diagnostics_json FROM snapshot_repository ORDER BY singleton")]);
|
|
@@ -335,8 +336,8 @@ function parseCurrent(text, contract) {
|
|
|
335
336
|
const value = JSON.parse(text);
|
|
336
337
|
if (!isRecord(value))
|
|
337
338
|
throw new Error("snapshot current facts are not an object");
|
|
338
|
-
assertClosedKeys(value, ["
|
|
339
|
-
if (value.contract !== contract || typeof value.
|
|
339
|
+
assertClosedKeys(value, ["contractHead", "contract", "phase", "intent", "receiptSegments", "seal", "bootstrapReplay", "petition", "verdict", "pins", "effectiveScope", "causal", "resources"], "snapshot current facts");
|
|
340
|
+
if (value.contract !== contract || typeof value.contractHead !== "string" || !isRecord(value.resources)) {
|
|
340
341
|
throw new Error("snapshot current facts disagree with their row key");
|
|
341
342
|
}
|
|
342
343
|
return value;
|
|
@@ -387,11 +388,8 @@ function validateContractConsistency(database, input, pin, summaryOnly = false)
|
|
|
387
388
|
const diagnostics = parseDiagnostics(diagnosticRow.diagnosticsJson, input.contract);
|
|
388
389
|
if ((input.status === "healthy") !== (diagnostics.length === 0))
|
|
389
390
|
throw new Error("snapshot status disagrees with diagnostics");
|
|
390
|
-
if (summaryOnly)
|
|
391
|
-
if (model.observedLedgerHead !== pin.head)
|
|
392
|
-
throw new Error("snapshot current summary disagrees with pin");
|
|
391
|
+
if (summaryOnly)
|
|
393
392
|
return model;
|
|
394
|
-
}
|
|
395
393
|
const fold = JSON.parse(input.foldJson);
|
|
396
394
|
if (!isRecord(fold) || fold.contract !== input.contract || !Array.isArray(fold.entryObjectIds))
|
|
397
395
|
throw new Error("snapshot fold is invalid");
|
|
@@ -399,7 +397,7 @@ function validateContractConsistency(database, input, pin, summaryOnly = false)
|
|
|
399
397
|
if (fold.entryObjectIds.length !== objectIds.length || fold.entryObjectIds.some((oid, index) => oid !== objectIds[index])) {
|
|
400
398
|
throw new Error("snapshot fold and lifecycle history disagree");
|
|
401
399
|
}
|
|
402
|
-
if (model.
|
|
400
|
+
if (model.contractHead !== objectIds.at(-1) || model.phase !== fold.phase || model.pins.base !== fold.bind?.data?.base) {
|
|
403
401
|
throw new Error("snapshot current facts disagree with fold or pin");
|
|
404
402
|
}
|
|
405
403
|
const typedFold = fold;
|
|
@@ -472,8 +470,8 @@ function storedContract(database, contract) {
|
|
|
472
470
|
diagnostics: parseDiagnostics(row.diagnosticsJson, contract),
|
|
473
471
|
};
|
|
474
472
|
}
|
|
475
|
-
function atPin(model,
|
|
476
|
-
return model
|
|
473
|
+
function atPin(model, _head) {
|
|
474
|
+
return model;
|
|
477
475
|
}
|
|
478
476
|
export async function readPinnedCurrentContract(cwd, observedHead, contract, signal) {
|
|
479
477
|
const databasePath = await repositoryCurrentStateSnapshotPath(cwd, observedHead, signal);
|
|
@@ -633,7 +631,7 @@ export async function readPinnedRepositoryLifecycle(cwd, observedHead, signal, o
|
|
|
633
631
|
validatePinReceipt(database, pin);
|
|
634
632
|
validateRepositoryReceipts(database, rows.map((row) => row.contract), options.summaryOnly === true, options.includeTopology !== false);
|
|
635
633
|
return {
|
|
636
|
-
observedLedgerHead: pin.head,
|
|
634
|
+
observedLedgerHead: pin.head === null ? null : repositoryLedgerHead(pin.head),
|
|
637
635
|
objectFormat: pin.objectFormat,
|
|
638
636
|
emptyTree: pin.emptyTree,
|
|
639
637
|
contracts: rows.flatMap((row) => {
|
|
@@ -855,14 +853,14 @@ export async function advanceRepositoryCurrentState(cwd, pinnedHead, observedHea
|
|
|
855
853
|
const prerequisite = database.prepare("SELECT status, fold_json AS foldJson FROM snapshot_contracts WHERE contract = ?").get(dependency);
|
|
856
854
|
if (!prerequisite || prerequisite.status !== "healthy" || prerequisite.foldJson === null)
|
|
857
855
|
return true;
|
|
858
|
-
return
|
|
856
|
+
return false;
|
|
859
857
|
});
|
|
860
858
|
if (unavailable.length > 0) {
|
|
861
859
|
const diagnostic = {
|
|
862
860
|
objectId: first.objectId,
|
|
863
861
|
code: "causal-prerequisite",
|
|
864
862
|
contract,
|
|
865
|
-
detail: `after prerequisites are not admitted
|
|
863
|
+
detail: `after prerequisites are not admitted healthy contracts: ${unavailable.join(", ")}`,
|
|
866
864
|
};
|
|
867
865
|
database.prepare(`
|
|
868
866
|
INSERT INTO snapshot_contracts(contract, status, current_json, fold_json, diagnostics_json)
|
|
@@ -944,7 +942,6 @@ export async function advanceRepositoryCurrentState(cwd, pinnedHead, observedHea
|
|
|
944
942
|
database.exec("ROLLBACK");
|
|
945
943
|
return false;
|
|
946
944
|
}
|
|
947
|
-
database.prepare("UPDATE snapshot_contracts SET current_json = json_set(current_json, '$.observedLedgerHead', ?) WHERE current_json IS NOT NULL").run(observedHead);
|
|
948
945
|
database.prepare("UPDATE snapshot_pin SET ledger_head = ? WHERE singleton = 1").run(observedHead);
|
|
949
946
|
writeIntegrityReceipts(database);
|
|
950
947
|
database.exec("COMMIT");
|
|
@@ -216,15 +216,14 @@ function validateAfterPrerequisites(ledger, contracts, isolatedContracts, diseas
|
|
|
216
216
|
const bindTransaction = containingTransactions.get(bindObjectId);
|
|
217
217
|
const ready = state.bind.data.after.every((dependency) => {
|
|
218
218
|
const prerequisite = contracts.get(dependency);
|
|
219
|
-
const
|
|
219
|
+
const prerequisiteBindTransaction = prerequisite === undefined
|
|
220
220
|
? undefined
|
|
221
|
-
: containingTransactions.get(prerequisite
|
|
221
|
+
: containingTransactions.get(prerequisite.entryObjectIds[0]);
|
|
222
222
|
return prerequisite !== undefined &&
|
|
223
223
|
valid.has(dependency) &&
|
|
224
|
-
|
|
225
|
-
terminalTransaction !== undefined &&
|
|
224
|
+
prerequisiteBindTransaction !== undefined &&
|
|
226
225
|
bindTransaction !== undefined &&
|
|
227
|
-
isTypedLedgerAncestor(ledger,
|
|
226
|
+
isTypedLedgerAncestor(ledger, prerequisiteBindTransaction, bindTransaction);
|
|
228
227
|
});
|
|
229
228
|
if (ready) {
|
|
230
229
|
valid.add(contract);
|
|
@@ -240,7 +239,7 @@ function validateAfterPrerequisites(ledger, contracts, isolatedContracts, diseas
|
|
|
240
239
|
objectId: bindObjectId,
|
|
241
240
|
code: "causal-prerequisite",
|
|
242
241
|
contract,
|
|
243
|
-
detail: `after
|
|
242
|
+
detail: `after prerequisite binds were not in typed ancestry: ${state.bind.data.after.join(", ")}`,
|
|
244
243
|
});
|
|
245
244
|
contracts.delete(contract);
|
|
246
245
|
isolatedContracts.add(contract);
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export function repositoryLedgerHead(value) {
|
|
2
|
+
return value;
|
|
3
|
+
}
|
|
4
|
+
export function lifecycleTransactionId(value) {
|
|
5
|
+
return value;
|
|
6
|
+
}
|
|
7
|
+
export function contractHead(value) {
|
|
8
|
+
return value;
|
|
9
|
+
}
|
|
10
|
+
export function carrierDigest(value) {
|
|
11
|
+
if (!/^[0-9a-f]{64}$/.test(value)) {
|
|
12
|
+
throw new Error("carrier digest must be a lowercase SHA-256 hex value");
|
|
13
|
+
}
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
export function carrierInputIdentity(input) {
|
|
17
|
+
return {
|
|
18
|
+
surface: "carrier",
|
|
19
|
+
contractId: input.contractId,
|
|
20
|
+
contractHead: input.contractHead,
|
|
21
|
+
bodyDigest: input.bodyDigest,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export function carrierRetirementInputIdentity(input) {
|
|
25
|
+
return { surface: "carrier-retirement", ...input };
|
|
26
|
+
}
|
|
27
|
+
export function worktreeInputIdentity(input) {
|
|
28
|
+
return {
|
|
29
|
+
surface: "worktree",
|
|
30
|
+
contractId: input.contractId,
|
|
31
|
+
contractHead: input.contractHead,
|
|
32
|
+
deliveryHead: input.deliveryHead,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export function contractViewInputIdentity(input) {
|
|
36
|
+
return {
|
|
37
|
+
surface: "contract-view",
|
|
38
|
+
contractId: input.contractId,
|
|
39
|
+
contractHead: input.contractHead,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export function narrowIgnoreInputIdentity(input) {
|
|
43
|
+
return { surface: "narrow-ignore", ...input };
|
|
44
|
+
}
|
|
45
|
+
export function renewDeliveryInputIdentity(input) {
|
|
46
|
+
return { surface: "renew-delivery", ...input };
|
|
47
|
+
}
|
|
48
|
+
export function taskBoardInputIdentity(input) {
|
|
49
|
+
return { surface: "task-board", ...input };
|
|
50
|
+
}
|
|
51
|
+
export function deliveryRefInputIdentity(input) {
|
|
52
|
+
return { surface: "delivery-ref", ...input };
|
|
53
|
+
}
|
|
54
|
+
export function targetWorktreeInputIdentity(input) {
|
|
55
|
+
return { surface: "target-worktree", ...input };
|
|
56
|
+
}
|
|
57
|
+
/** Structural equality keeps surface comparisons independent of process-local receipts. */
|
|
58
|
+
export function sameInputIdentity(left, right) {
|
|
59
|
+
return left !== null && JSON.stringify(left) === JSON.stringify(right);
|
|
60
|
+
}
|
|
@@ -295,12 +295,12 @@ export function activeWorkspaceHereContracts(read) {
|
|
|
295
295
|
}
|
|
296
296
|
/** Runtime ignore projection for one worktree from a single typed repository fold. */
|
|
297
297
|
export function inventoryIgnorePatternsForWorktree(read, targetLifecycle) {
|
|
298
|
-
if (read.observedLedgerHead !== targetLifecycle.observedLedgerHead) {
|
|
299
|
-
throw new Error("narrow-ignore inventory requires one repository-ledger observation");
|
|
300
|
-
}
|
|
301
298
|
const acceptedTarget = read.contracts.find(({ contract }) => contract === targetLifecycle.contract);
|
|
302
299
|
if (!acceptedTarget)
|
|
303
300
|
throw new Error(`contract ${targetLifecycle.contract} is absent from narrow-ignore inventory`);
|
|
301
|
+
if (acceptedTarget.contractHead !== targetLifecycle.contractHead) {
|
|
302
|
+
throw new Error("narrow-ignore inventory requires the current contract semantic head");
|
|
303
|
+
}
|
|
304
304
|
const target = foldedWorktreeCoordinate(acceptedTarget);
|
|
305
305
|
const required = target.workspace === "here"
|
|
306
306
|
? activeWorkspaceHereContracts(read).length > 0
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { openTaskAdmissionEvidenceReader, readTaskAdmissionEvidenceAtCommit, } from "../task/settlement-git.js";
|
|
2
2
|
import { repositoryDeliveryRef, } from "./codec.js";
|
|
3
|
+
import { bindEffectiveScope } from "./fold.js";
|
|
3
4
|
import { observeRepositoryLedgerHead, readAcceptedRepositoryFold } from "./accepted-fold-read.js";
|
|
4
5
|
import { advanceRepositoryCurrentState, publishRepositoryCurrentState, readPinnedCurrentContract, readPinnedRepositoryLifecycle, readRepositoryCurrentStateNodeIds, readRepositoryCurrentStatePin, readRepositoryCurrentStatePredecessor } from "./current-state-store.js";
|
|
5
6
|
import { IncrementalLedgerValidationError, readIncrementalRepositorySegment } from "./incremental-read.js";
|
|
6
7
|
import { RepositoryObjectError } from "./git-object-store.js";
|
|
8
|
+
import { contractHead, repositoryLedgerHead } from "./identity.js";
|
|
7
9
|
/** Public lifecycle state derived only from the accepted repository-ledger fold. */
|
|
8
10
|
export function currentLifecycleState(model) {
|
|
9
11
|
switch (model.phase) {
|
|
@@ -22,6 +24,50 @@ export function currentLifecycleState(model) {
|
|
|
22
24
|
return { state: "forfeited" };
|
|
23
25
|
}
|
|
24
26
|
}
|
|
27
|
+
/** Purely advance an admitted repository snapshot with its just-committed bind. */
|
|
28
|
+
export function advanceRepositorySnapshotWithBind(input) {
|
|
29
|
+
const { repository, ledgerHead, entryObjectId, entry } = input;
|
|
30
|
+
const bootstrap = entry.data.workspace === "worktree" ? entry.data.bootstrap : undefined;
|
|
31
|
+
const model = deepFreeze({
|
|
32
|
+
contractHead: contractHead(entryObjectId),
|
|
33
|
+
contract: entry.contract,
|
|
34
|
+
phase: "active",
|
|
35
|
+
intent: { arc: 0, openedBy: entry.actor, title: entry.data.title, objective: entry.data.objective, brief: null },
|
|
36
|
+
receiptSegments: [],
|
|
37
|
+
seal: null,
|
|
38
|
+
bootstrapReplay: bootstrap
|
|
39
|
+
? { state: "pending", plan: { command: [...bootstrap.command], timeoutMs: bootstrap.timeoutMs } }
|
|
40
|
+
: { state: "not-planned" },
|
|
41
|
+
petition: { state: "none" },
|
|
42
|
+
verdict: null,
|
|
43
|
+
pins: { base: entry.data.base, delivery: entry.data.base, target: { ref: entry.data.target, approvedHead: null } },
|
|
44
|
+
effectiveScope: bindEffectiveScope(entry),
|
|
45
|
+
causal: { after: [...(entry.data.after ?? [])], needs: null },
|
|
46
|
+
resources: {
|
|
47
|
+
deliveryRef: repositoryDeliveryRef(entry.contract),
|
|
48
|
+
worktree: entry.data.workspace === "worktree" ? { place: entry.data.place, state: "held" } : null,
|
|
49
|
+
tasks: [...(entry.data.taskIds ?? [])].map((taskId) => ({ taskId, state: "held" })),
|
|
50
|
+
},
|
|
51
|
+
history: [{ objectId: entryObjectId, entry: structuredClone(entry) }],
|
|
52
|
+
});
|
|
53
|
+
return deepFreeze({
|
|
54
|
+
...structuredClone(repository),
|
|
55
|
+
observedLedgerHead: ledgerHead,
|
|
56
|
+
contracts: [...repository.contracts, model].sort((left, right) => left.contract.localeCompare(right.contract)),
|
|
57
|
+
topology: {
|
|
58
|
+
head: ledgerHead,
|
|
59
|
+
nodes: [...repository.topology.nodes, {
|
|
60
|
+
objectId: ledgerHead,
|
|
61
|
+
type: "transaction",
|
|
62
|
+
ledgerParents: repository.observedLedgerHead ? [repository.observedLedgerHead] : [],
|
|
63
|
+
verb: "bind",
|
|
64
|
+
contract: entry.contract,
|
|
65
|
+
entryObjectIds: [entryObjectId],
|
|
66
|
+
finalEntryObjectId: entryObjectId,
|
|
67
|
+
}],
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
}
|
|
25
71
|
function deepFreeze(value) {
|
|
26
72
|
if (value === null || typeof value !== "object" || Object.isFrozen(value))
|
|
27
73
|
return value;
|
|
@@ -199,14 +245,14 @@ function bootstrapReplay(state) {
|
|
|
199
245
|
}
|
|
200
246
|
throw new Error(`failed bootstrap for ${state.contract} has no folded replay disposition`);
|
|
201
247
|
}
|
|
202
|
-
function lifecycleModel(
|
|
248
|
+
function lifecycleModel(_observedLedgerHead, state, nodes, taskAdmissionEvidence) {
|
|
203
249
|
const history = acceptedHistory(state, nodes);
|
|
204
250
|
const verdict = findLastRow(history, "verdict");
|
|
205
251
|
const released = state.terminalObjectId !== null;
|
|
206
252
|
const taskReleased = state.terminal?.kind === "forfeit";
|
|
207
253
|
const selectedTasks = [...(state.bind.data.taskIds ?? [])];
|
|
208
254
|
return deepFreeze({
|
|
209
|
-
|
|
255
|
+
contractHead: contractHead(state.entryObjectIds.at(-1)),
|
|
210
256
|
contract: state.contract,
|
|
211
257
|
phase: state.phase,
|
|
212
258
|
intent: currentIntent(state, history),
|
|
@@ -359,7 +405,9 @@ async function recoverRepositoryLifecycle(cwd, options = {}) {
|
|
|
359
405
|
});
|
|
360
406
|
}
|
|
361
407
|
export async function readRepositoryLifecycle(cwd, options = {}) {
|
|
362
|
-
const observe = options.
|
|
408
|
+
const observe = options.observedHead !== undefined
|
|
409
|
+
? async () => options.observedHead
|
|
410
|
+
: options.observeHead ?? observeRepositoryLedgerHead;
|
|
363
411
|
const observedHead = await observe(cwd);
|
|
364
412
|
const injected = options.observeHead !== undefined || options.readTaskAdmissionEvidence !== undefined;
|
|
365
413
|
if (!injected) {
|
|
@@ -374,8 +422,8 @@ export async function readRepositoryLifecycle(cwd, options = {}) {
|
|
|
374
422
|
const segment = await readIncrementalRepositorySegment(cwd, priorHead, observedHead, options.signal, acceptedObjectIds ?? undefined);
|
|
375
423
|
if (segment && await advanceRepositoryCurrentState(cwd, priorHead, observedHead, segment, {
|
|
376
424
|
signal: options.signal,
|
|
377
|
-
confirmHead: () => observeRepositoryLedgerHead(cwd),
|
|
378
|
-
materialize: (state, history) => materializeIncrementalContract(cwd, observedHead, state, history),
|
|
425
|
+
confirmHead: options.observedHead !== undefined ? async () => observedHead : () => observeRepositoryLedgerHead(cwd),
|
|
426
|
+
materialize: (state, history) => materializeIncrementalContract(cwd, repositoryLedgerHead(observedHead), state, history),
|
|
379
427
|
})) {
|
|
380
428
|
const advanced = await readPinnedRepositoryLifecycle(cwd, observedHead, options.signal, { includeTopology: options.includeTopology });
|
|
381
429
|
if (advanced)
|
|
@@ -404,7 +452,7 @@ export async function readRepositoryLifecycle(cwd, options = {}) {
|
|
|
404
452
|
if (!injected) {
|
|
405
453
|
const published = await publishRepositoryCurrentState(cwd, recovered, {
|
|
406
454
|
signal: options.signal,
|
|
407
|
-
confirmHead: () => observeRepositoryLedgerHead(cwd),
|
|
455
|
+
confirmHead: options.observedHead !== undefined ? async () => observedHead : () => observeRepositoryLedgerHead(cwd),
|
|
408
456
|
fold: recoveredFold,
|
|
409
457
|
});
|
|
410
458
|
if (!published)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { runGitProcess } from "../../git/core.js";
|
|
2
2
|
import { commitRefTransaction, } from "../../git/refs.js";
|
|
3
3
|
import { REPOSITORY_LEDGER_REF, evidenceObjectId, formatLifecycleEntry, formatLifecycleTransaction, repositoryDeliveryRef, } from "./codec.js";
|
|
4
|
-
import { assertHostObjectId, assertReadableCommit, deriveHostGitCoordinates, } from "./git-object-store.js";
|
|
4
|
+
import { assertHostObjectId, assertReadableCommit, assertReadableCommitClosure, deriveHostGitCoordinates, } from "./git-object-store.js";
|
|
5
5
|
import { assertHonestClaimEvidence } from "./claim-evidence.js";
|
|
6
6
|
import { transactionMembershipError } from "./transaction-membership.js";
|
|
7
7
|
function replayFailure(effect, error) {
|
|
@@ -76,7 +76,7 @@ async function assertAuthoritativeCommitTipClosure(cwd, coordinates, operations)
|
|
|
76
76
|
if (operation.kind !== "update" || operation.ref === REPOSITORY_LEDGER_REF || checked.has(operation.newOid))
|
|
77
77
|
continue;
|
|
78
78
|
assertHostObjectId(coordinates, operation.newOid);
|
|
79
|
-
await
|
|
79
|
+
await assertReadableCommitClosure(cwd, operation.newOid);
|
|
80
80
|
checked.add(operation.newOid);
|
|
81
81
|
}
|
|
82
82
|
}
|
|
@@ -248,6 +248,7 @@ async function publishLifecycleWrite(cwd, builtTransaction, final, ownedOperatio
|
|
|
248
248
|
completion: replay.status,
|
|
249
249
|
ledgerHead: builtTransaction.objectId,
|
|
250
250
|
entry: final.entry,
|
|
251
|
+
entryObjectId: final.objectId,
|
|
251
252
|
authoritativeOperations,
|
|
252
253
|
authorityProof: transaction.proof,
|
|
253
254
|
authorityObservations: transaction.observations,
|
|
@@ -280,6 +281,9 @@ export async function publishBindTransaction(input) {
|
|
|
280
281
|
{ kind: "update", ref: input.delivery.ref, newOid: input.delivery.head, expectedOldOid: null },
|
|
281
282
|
];
|
|
282
283
|
if (input.taskBoard) {
|
|
284
|
+
if (input.taskBoard.ref !== input.entry.data.target) {
|
|
285
|
+
throw new Error("task-selected bind task-board ref must equal the entry target");
|
|
286
|
+
}
|
|
283
287
|
operations.push({
|
|
284
288
|
kind: "update",
|
|
285
289
|
ref: input.taskBoard.ref,
|
|
@@ -313,11 +317,12 @@ export async function publishAuthoritativeTransaction(cwd, transaction, options
|
|
|
313
317
|
const entry = final.entry;
|
|
314
318
|
const operations = [];
|
|
315
319
|
if (transaction.verb === "renew" && entry.kind === "renew") {
|
|
316
|
-
assertAuthority("delivery" in transaction, "renew transaction must pin its delivery
|
|
320
|
+
assertAuthority("delivery" in transaction && "target" in transaction, "renew transaction must pin its target and delivery refs");
|
|
321
|
+
assertAuthority(transaction.target.expectedOid === entry.data.newBase, "renew target CAS must equal newBase");
|
|
317
322
|
assertAuthority(transaction.delivery.ref === repositoryDeliveryRef(entry.contract), "renew delivery ref does not name its contract");
|
|
318
323
|
assertAuthority(transaction.delivery.expectedOid === entry.data.oldHead, "renew delivery CAS must equal oldHead");
|
|
319
324
|
assertAuthority(entry.data.oldHead !== entry.data.newHead, "renew must replace the delivery head");
|
|
320
|
-
operations.push({ kind: "update", ref: transaction.delivery.ref, expectedOldOid: transaction.delivery.expectedOid, newOid: entry.data.newHead });
|
|
325
|
+
operations.push({ kind: "verify", ref: transaction.target.ref, expectedOldOid: transaction.target.expectedOid }, { kind: "update", ref: transaction.delivery.ref, expectedOldOid: transaction.delivery.expectedOid, newOid: entry.data.newHead });
|
|
321
326
|
}
|
|
322
327
|
else if (transaction.verb === "forfeit") {
|
|
323
328
|
assertAuthority(entry.kind === "forfeit", "forfeit transaction must end in a forfeit entry");
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export function beginConnectionBoundRunnerClose(database, lease) {
|
|
2
|
+
database.exec("BEGIN IMMEDIATE");
|
|
3
|
+
let open = true;
|
|
4
|
+
let leaseOwnership = "retained";
|
|
5
|
+
const rollback = () => {
|
|
6
|
+
open = false;
|
|
7
|
+
database.exec("ROLLBACK");
|
|
8
|
+
};
|
|
9
|
+
return {
|
|
10
|
+
get leaseOwnership() { return leaseOwnership; },
|
|
11
|
+
commit() {
|
|
12
|
+
if (!open)
|
|
13
|
+
throw new Error("connection-bound runner close is not open");
|
|
14
|
+
try {
|
|
15
|
+
lease.close();
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
leaseOwnership = "unknown";
|
|
19
|
+
rollback();
|
|
20
|
+
throw error;
|
|
21
|
+
}
|
|
22
|
+
leaseOwnership = "consumed";
|
|
23
|
+
try {
|
|
24
|
+
database.exec("COMMIT");
|
|
25
|
+
open = false;
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
rollback();
|
|
29
|
+
throw error;
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
rollback() {
|
|
33
|
+
if (leaseOwnership !== "retained") {
|
|
34
|
+
throw new Error(`connection-bound runner lease ownership is ${leaseOwnership}`);
|
|
35
|
+
}
|
|
36
|
+
if (!open)
|
|
37
|
+
throw new Error("connection-bound runner close is no longer open");
|
|
38
|
+
rollback();
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|