@astrosheep/keiyaku 2.9.8 → 2.9.10
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 +50 -182
- package/build/.tsbuildinfo +1 -1
- package/build/agents/harness/control-types.js +18 -1
- package/build/agents/harness/index.js +2 -0
- package/build/agents/harness/outcome-error.js +81 -0
- package/build/agents/harness/projection.js +10 -7
- package/build/agents/harness/router.js +5 -1
- package/build/agents/providers/claude-agent-sdk/adapter.js +18 -4
- package/build/agents/providers/claude-agent-sdk/registration.js +11 -1
- package/build/agents/providers/claude-agent-sdk/session.js +19 -5
- package/build/agents/providers/codex-app-server/adapter.js +11 -6
- package/build/agents/providers/codex-app-server/events.js +67 -6
- package/build/agents/providers/codex-app-server/registration.js +11 -1
- package/build/agents/providers/opencode-sdk/adapter.js +8 -1
- package/build/agents/providers/opencode-sdk/events.js +24 -2
- package/build/agents/providers/opencode-sdk/registration.js +11 -1
- package/build/agents/providers/pi/checkpoint.js +9 -1
- package/build/agents/providers/pi/events.js +2 -0
- package/build/agents/providers/pi/registration.js +14 -1
- package/build/cli/commands/akuma/akuma/handler.js +1 -1
- package/build/cli/commands/akuma/akuma/meta.js +3 -3
- package/build/cli/commands/akuma/catalog.js +2 -2
- package/build/cli/commands/akuma/{view → show}/handler.js +5 -5
- package/build/cli/commands/akuma/{view → show}/meta.js +3 -3
- package/build/cli/commands/akuma.js +1 -1
- package/build/cli/commands/contract/amend/handler.js +5 -4
- package/build/cli/commands/contract/arc/handler.js +5 -4
- package/build/cli/commands/contract/audit/handler.js +2 -2
- package/build/cli/commands/contract/bind/handler.js +12 -3
- package/build/cli/commands/contract/bind/meta.js +45 -15
- package/build/cli/commands/contract/forfeit/handler.js +9 -5
- package/build/cli/commands/contract/log/handler.js +2 -2
- package/build/cli/commands/contract/petition/handler.js +34 -7
- package/build/cli/commands/contract/renew/handler.js +7 -7
- package/build/cli/commands/contract/renew/meta.js +3 -3
- package/build/cli/commands/metadata.js +10 -6
- package/build/cli/commands/projection/call/meta.js +1 -1
- package/build/cli/commands/projection/catalog.js +2 -0
- package/build/cli/commands/projection/history/handler.js +21 -0
- package/build/cli/commands/projection/history/meta.js +9 -0
- package/build/cli/commands/projection/shared.js +3 -4
- package/build/cli/commands/projection/status/handler.js +3 -3
- package/build/cli/commands/projection/wait/handler.js +20 -11
- package/build/cli/commands/task/add/handler.js +14 -5
- package/build/cli/commands/task/add/meta.js +2 -2
- package/build/cli/commands/task/catalog.js +4 -0
- package/build/cli/commands/task/done/handler.js +1 -4
- package/build/cli/commands/task/done/meta.js +1 -1
- package/build/cli/commands/task/drop/meta.js +1 -1
- package/build/cli/commands/task/hold/handler.js +3 -0
- package/build/cli/commands/task/hold/meta.js +8 -0
- package/build/cli/commands/task/log/handler.js +1 -1
- package/build/cli/commands/task/log/meta.js +2 -2
- package/build/cli/commands/task/resume/handler.js +3 -0
- package/build/cli/commands/task/resume/meta.js +8 -0
- package/build/cli/commands/task/shared.js +39 -14
- package/build/cli/commands/task/show/handler.js +1 -1
- package/build/cli/commands/task/show/meta.js +1 -1
- package/build/cli/commands/task/start/meta.js +1 -1
- package/build/cli/commands/task/stop/handler.js +1 -4
- package/build/cli/commands/task/stop/meta.js +1 -1
- package/build/cli/commands/task/update/handler.js +11 -3
- package/build/cli/commands/task/update/meta.js +3 -3
- package/build/cli/completion.js +3 -2
- package/build/cli/flags.js +51 -14
- package/build/cli/help.js +4 -1
- package/build/cli/index.js +40 -25
- package/build/cli/parse-flags.js +40 -7
- package/build/cli/parse-metadata.js +5 -2
- package/build/cli/parse-selectors.js +2 -3
- package/build/cli/parse.js +40 -6
- package/build/cli/projection-address.js +12 -10
- package/build/cli/render/amendment-warning.js +8 -0
- package/build/cli/render/arc.js +6 -2
- package/build/cli/render/audit.js +400 -81
- package/build/cli/render/kanshi.js +242 -79
- package/build/cli/render/lifecycle.js +56 -0
- package/build/cli/render/misc.js +24 -28
- package/build/cli/render/petition.js +5 -0
- package/build/cli/render/projection-activity.js +65 -29
- package/build/cli/render/projection-history.js +64 -0
- package/build/cli/render/shared.js +23 -9
- package/build/cli/render/status.js +13 -35
- package/build/cli/render/task.js +51 -26
- package/build/cli/render/terminal-failure.js +3 -1
- package/build/cli/render/wait.js +45 -69
- package/build/cli/subagent-guard.js +1 -1
- package/build/cli/types.js +1 -1
- package/build/config/env-keys.js +3 -1
- package/build/config/env.js +24 -1
- package/build/config/settings/schema.js +3 -1
- package/build/core/address-carrier.js +89 -0
- package/build/core/addressing.js +255 -196
- package/build/core/amend.js +115 -38
- package/build/core/amendment-criteria.js +17 -0
- package/build/core/amendment-warning.js +10 -0
- package/build/core/arc.js +184 -83
- package/build/core/audit/candidate.js +110 -9
- package/build/core/audit/coordinates.js +120 -65
- package/build/core/audit/evidence.js +19 -23
- package/build/core/audit/facade.js +34 -11
- package/build/core/audit/report.js +5 -5
- package/build/core/bind-reconciliation.js +390 -0
- package/build/core/bind-workspace.js +12 -20
- package/build/core/bind.js +242 -273
- package/build/core/call/call.js +1 -0
- package/build/core/call/context.js +46 -71
- package/build/core/call/prompt.js +6 -3
- package/build/core/context.js +49 -35
- package/build/core/contract-carrier-runtime.js +408 -0
- package/build/core/contract-carrier.js +270 -0
- package/build/core/contract-view.js +33 -27
- package/build/core/contract.js +5 -1
- package/build/core/derived-replay.js +207 -0
- package/build/core/draft.js +125 -57
- package/build/core/forfeit.js +113 -56
- package/build/core/identifier-slug.js +63 -13
- package/build/core/ids.js +4 -4
- package/build/core/lifecycle-history.js +35 -0
- package/build/core/lifecycle-recovery.js +117 -0
- package/build/core/lifecycle-runner.js +377 -0
- package/build/core/log.js +37 -11
- package/build/core/outcome-base.js +181 -0
- package/build/core/process-group.js +139 -0
- package/build/core/projection/generation/database.js +82 -7
- package/build/core/projection/generation/model.js +9 -36
- package/build/core/projection/generation/projection-generation-continuation.js +10 -22
- package/build/core/projection/generation/projection-generation-execution.js +13 -6
- package/build/core/projection/index.js +5 -4
- package/build/core/projection/projection-activity.js +29 -8
- package/build/core/projection/projection-alias.js +23 -10
- package/build/core/projection/projection-core.js +21 -7
- package/build/core/projection/projection-execution-observer.js +91 -39
- package/build/core/projection/projection-history.js +474 -0
- package/build/core/projection/projection-runner-lock.js +12 -0
- package/build/core/projection/projection-status-observation.js +365 -0
- package/build/core/projection/projection-status.js +35 -337
- package/build/core/projection/projection-terminal-failure.js +13 -8
- package/build/core/projection/projection-wait.js +7 -2
- package/build/core/projection/tell/launch-store.js +244 -0
- package/build/core/projection/tell/model.js +3 -3
- package/build/core/projection/tell/store.js +70 -118
- package/build/core/registry.js +22 -87
- package/build/core/renew-build.js +424 -0
- package/build/core/renew-plan.js +239 -0
- package/build/core/renew-rewrite.js +77 -0
- package/build/core/renew.js +295 -520
- package/build/core/repository-ledger/accepted-fold-read.js +19 -0
- package/build/core/repository-ledger/accepted-tail.js +116 -0
- package/build/core/repository-ledger/admission.js +38 -0
- package/build/core/repository-ledger/authoritative-transaction.js +1 -0
- package/build/core/repository-ledger/claim-evidence.js +147 -0
- package/build/core/repository-ledger/codec.js +297 -0
- package/build/core/repository-ledger/current-state-store.js +853 -0
- package/build/core/repository-ledger/fold-repository.js +375 -0
- package/build/core/repository-ledger/fold.js +248 -0
- package/build/core/repository-ledger/git-object-store.js +243 -0
- package/build/core/repository-ledger/history-read.js +15 -0
- package/build/core/repository-ledger/incremental-read.js +153 -0
- package/build/core/repository-ledger/index.js +15 -0
- package/build/core/repository-ledger/inventory-plan.js +75 -0
- package/build/core/repository-ledger/inventory.js +310 -0
- package/build/core/repository-ledger/publication-admission.js +125 -0
- package/build/core/repository-ledger/publication.js +353 -0
- package/build/core/repository-ledger/read-model.js +469 -0
- package/build/core/repository-ledger/transaction-membership.js +78 -0
- package/build/core/repository-ledger/traversal.js +404 -0
- package/build/core/repository-ledger/write-transaction.js +339 -0
- package/build/core/seal.js +168 -161
- package/build/core/settlement/claim-delivery.js +42 -215
- package/build/core/settlement/claim.js +235 -153
- package/build/core/settlement/index.js +4 -4
- package/build/core/settlement/petition-claim-gates.js +52 -33
- package/build/core/settlement/petition-forfeit.js +12 -5
- package/build/core/settlement/petition-head-guard.js +8 -5
- package/build/core/settlement/petition-preview.js +68 -56
- package/build/core/settlement/petition-window.js +50 -0
- package/build/core/settlement/petition.js +252 -131
- package/build/core/settlement/queue-read-model.js +72 -95
- package/build/core/settlement/queue-seat-allocation.js +35 -126
- package/build/core/settlement/queue.js +1 -1
- package/build/core/settlement/settlement.js +172 -80
- package/build/core/settlement/verdict.js +112 -66
- package/build/core/settlement/verification-coordination.js +305 -0
- package/build/core/settlement/verification-supervisor.js +275 -0
- package/build/core/settlement/verification.js +398 -31
- package/build/core/status/board.js +125 -127
- package/build/core/status/lifecycle.js +7 -80
- package/build/core/structured-query.js +23 -0
- package/build/core/target-ref.js +1 -1
- package/build/core/task/board.js +59 -24
- package/build/core/task/commands.js +181 -33
- package/build/core/task/coordinate.js +56 -0
- package/build/core/task/document.js +25 -9
- package/build/core/task/index.js +7 -4
- package/build/core/task/query.js +17 -27
- package/build/core/task/settlement-git.js +164 -179
- package/build/core/task/settlement-policy.js +15 -11
- package/build/core/task/source-board.js +31 -13
- package/build/core/task/task-bind-preparation.js +193 -0
- package/build/core/task/task-contract.js +102 -193
- package/build/core/task/task-file-transaction.js +171 -0
- package/build/core/task/task-git-runtime.js +76 -14
- package/build/core/task/task-git-store.js +68 -169
- package/build/core/task/task-store-repository.js +13 -6
- package/build/core/task/task-worktree.js +6 -33
- package/build/core/task/task.js +1 -1
- package/build/core/task/tree.js +3 -3
- package/build/core/task/validation.js +4 -3
- package/build/core/transcripts.js +93 -1
- package/build/core/worktree-bootstrap.js +44 -36
- package/build/core/worktree-path.js +210 -77
- package/build/flow-error.js +7 -7
- package/build/generated/version.js +2 -2
- package/build/git/core.js +76 -13
- package/build/git/refs.js +168 -46
- package/build/git/streaming-batch.js +73 -13
- package/package.json +1 -1
- package/skills/keiyaku/SKILL.md +1 -1
- package/skills/keiyaku-akuma/SKILL.md +3 -3
- package/skills/keiyaku-task/SKILL.md +4 -2
- package/skills/keiyaku-workflow/SKILL.md +88 -203
- package/build/core/entry.js +0 -305
- package/build/core/ledger-batch.js +0 -194
- package/build/core/ledger.js +0 -84
- package/build/core/projection/leash.js +0 -87
- package/build/core/ref-log.js +0 -119
- package/build/core/renew-session.js +0 -128
- package/build/core/status/ledger-batch.js +0 -1
- package/build/core/status/reconciliation.js +0 -137
package/build/core/renew.js
CHANGED
|
@@ -1,566 +1,341 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { createGit, errorContainsAnyPattern, MISSING_HEAD_PATTERNS, parseRevListOutput, runGitProcess, wrapGitError } from "../git/core.js";
|
|
3
|
-
import { readLedger } from "./ledger.js";
|
|
4
|
-
import { hasStrandableProjectionGeneration } from "./projection/index.js";
|
|
5
|
-
import { refreshContractView } from "./contract-view.js";
|
|
6
|
-
import { appendRenewSettlement, appendScopeTerms, captureRenewSeal, captureSeal, findOpenArc, getFence, getMaxArc, receiptsAfterFence } from "./seal.js";
|
|
7
|
-
import { deriveContractState, isTerminalState } from "./status/lifecycle.js";
|
|
8
|
-
import { commissionTarget, latestBase } from "./status/drift.js";
|
|
9
|
-
import { findContractWorktreePath, findRegisteredContractWorktreePath, stableRepoRoot } from "./worktree-path.js";
|
|
1
|
+
import { FlowError } from "../flow-error.js";
|
|
10
2
|
import { getCurrentBranch, requireGitRepository } from "../git/branches.js";
|
|
11
|
-
import { assertCleanTrackedWorkingTree, getUnmergedPaths, hasActiveRebase } from "../git/worktree.js";
|
|
12
|
-
import { createRecoveryRef, deleteRecoveryRef } from "../git/refs.js";
|
|
13
|
-
import { resolveContractAddress, withResolvedAddress } from "./addressing.js";
|
|
14
3
|
import { ACTOR_IDENTITY } from "../keiyaku.js";
|
|
4
|
+
import { resolveContractAddress, withResolvedAddress } from "./addressing.js";
|
|
5
|
+
import { withContractCarrierSession } from "./contract-carrier.js";
|
|
6
|
+
import { runLifecycle } from "./lifecycle-runner.js";
|
|
7
|
+
import { committedLifecycleFacts, derivedCleanupFacts, isCompleteLifecycleOutcome, } from "./outcome-base.js";
|
|
8
|
+
import { abortDetachedRenewChain, readDetachedRenewScratch, renewScratchName, } from "./renew-rewrite.js";
|
|
15
9
|
import { persistResponseHistory } from "./transcripts.js";
|
|
16
|
-
import {
|
|
17
|
-
function
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return (await git.raw(["rev-parse", "--verify", `${rev}^{commit}`])).trim();
|
|
30
|
-
}
|
|
31
|
-
catch (error) {
|
|
32
|
-
throw wrapGitError(`rev-parse ${rev}`, error, cwd);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
async function resolveCommitOrNull(cwd, rev) {
|
|
36
|
-
const git = createGit(cwd);
|
|
37
|
-
try {
|
|
38
|
-
return (await git.raw(["rev-parse", "--verify", `${rev}^{commit}`])).trim();
|
|
39
|
-
}
|
|
40
|
-
catch (error) {
|
|
41
|
-
if (errorContainsAnyPattern(error, MISSING_HEAD_PATTERNS))
|
|
42
|
-
return null;
|
|
43
|
-
throw wrapGitError(`rev-parse ${rev}`, error, cwd);
|
|
10
|
+
import { admitRenew, checkCancellation, prepareRenew, sameAdmission, scopeEntry } from "./renew-plan.js";
|
|
11
|
+
async function persistRenewResponse(journal, facts, persist) {
|
|
12
|
+
if (!persist)
|
|
13
|
+
return;
|
|
14
|
+
let response;
|
|
15
|
+
await journal.run({
|
|
16
|
+
effect: "response-history-persistence",
|
|
17
|
+
idempotencyKey: `${facts.contractId}:${facts.ledgerHead}:response-history-persistence`,
|
|
18
|
+
run: async () => { response = await persist(facts); return response; },
|
|
19
|
+
});
|
|
20
|
+
if (response) {
|
|
21
|
+
facts.responsePath = response.responsePath;
|
|
22
|
+
facts.artifactId = response.artifactId;
|
|
44
23
|
}
|
|
45
24
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
25
|
+
async function executeRenew(input, continuation) {
|
|
26
|
+
const execution = await runLifecycle({
|
|
27
|
+
cwd: input.cwd,
|
|
28
|
+
contractId: input.contractId,
|
|
29
|
+
mutation: {
|
|
30
|
+
verb: "renew",
|
|
31
|
+
matchesAccepted: (transaction) => {
|
|
32
|
+
const accepted = transaction.entries.at(-1);
|
|
33
|
+
const expectedAmend = scopeEntry(input);
|
|
34
|
+
if (accepted?.kind === "amend") {
|
|
35
|
+
return transaction.entries.length === 1
|
|
36
|
+
&& expectedAmend !== null
|
|
37
|
+
&& accepted.data.amendment === expectedAmend.data.amendment
|
|
38
|
+
&& JSON.stringify(accepted.data.scopeDelta ?? null) === JSON.stringify(expectedAmend.data.scopeDelta ?? null);
|
|
39
|
+
}
|
|
40
|
+
if (accepted?.kind !== "renew")
|
|
41
|
+
return false;
|
|
42
|
+
const acceptedAmend = transaction.entries.find((entry) => entry.kind === "amend");
|
|
43
|
+
return JSON.stringify(acceptedAmend?.kind === "amend" ? acceptedAmend.data.scopeDelta ?? null : null)
|
|
44
|
+
=== JSON.stringify(expectedAmend?.data.scopeDelta ?? null);
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
signal: input.signal,
|
|
48
|
+
...(input.renewDeliveryProjector ? { renewDeliveryProjector: input.renewDeliveryProjector } : {}),
|
|
49
|
+
admit: (session, replay) => admitRenew(session, input, replay, continuation?.scratch),
|
|
50
|
+
prepare: (admission) => prepareRenew(admission.business, input, continuation),
|
|
51
|
+
revalidate: async ({ session, admission }) => {
|
|
52
|
+
const current = await admitRenew(session, input, admission.replay, continuation?.scratch);
|
|
53
|
+
if (!sameAdmission(current, admission.business)) {
|
|
54
|
+
return { code: "RENEW_COORDINATES_CHANGED", message: "renew coordinates changed during detached construction" };
|
|
67
55
|
}
|
|
68
|
-
|
|
69
|
-
|
|
56
|
+
},
|
|
57
|
+
effect: async ({ context, admission, prepared }) => {
|
|
58
|
+
if (admission.business.acceptedScopeReplay) {
|
|
59
|
+
const accepted = admission.business.acceptedScopeReplay;
|
|
60
|
+
const replayed = await context.replayCommitted({ transactionObjectId: accepted.transactionObjectId });
|
|
61
|
+
const value = {
|
|
62
|
+
...committedLifecycleFacts({ head: accepted.transactionObjectId, entryKind: "amend", contractId: input.contractId, carrier: replayed.carrier }),
|
|
63
|
+
contractId: input.contractId,
|
|
64
|
+
oldBase: admission.business.oldBase,
|
|
65
|
+
newBase: admission.business.newBase,
|
|
66
|
+
oldHead: admission.business.oldHead,
|
|
67
|
+
newHead: admission.business.oldHead,
|
|
68
|
+
ledgerHead: accepted.transactionObjectId,
|
|
69
|
+
alreadyFresh: true,
|
|
70
|
+
sealOid: null,
|
|
71
|
+
renewOid: null,
|
|
72
|
+
sealed: null,
|
|
73
|
+
carrier: replayed.carrier,
|
|
74
|
+
targetRoot: admission.business.targetRoot,
|
|
75
|
+
};
|
|
76
|
+
await persistRenewResponse(context.journal, value, input.persistResponse);
|
|
77
|
+
return { kind: "committed", commit: replayed, entryKind: "amend", value };
|
|
70
78
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
79
|
+
if (admission.business.acceptedReplay) {
|
|
80
|
+
const accepted = admission.business.acceptedReplay;
|
|
81
|
+
const replayed = await context.replayCommitted({ transactionObjectId: accepted.transactionObjectId });
|
|
82
|
+
const sealed = { ...accepted.seal.data, scopeViolationEvidence: [], ledgerHead: accepted.sealObjectId };
|
|
83
|
+
const value = {
|
|
84
|
+
...committedLifecycleFacts({ head: accepted.transactionObjectId, entryKind: "renew", contractId: input.contractId, carrier: replayed.carrier }),
|
|
85
|
+
contractId: input.contractId,
|
|
86
|
+
oldBase: accepted.entry.data.oldBase,
|
|
87
|
+
newBase: accepted.entry.data.newBase,
|
|
88
|
+
oldHead: accepted.entry.data.oldHead,
|
|
89
|
+
newHead: accepted.entry.data.newHead,
|
|
90
|
+
ledgerHead: accepted.transactionObjectId,
|
|
91
|
+
alreadyFresh: false,
|
|
92
|
+
sealOid: accepted.sealObjectId,
|
|
93
|
+
renewOid: accepted.renewObjectId,
|
|
94
|
+
sealed,
|
|
95
|
+
carrier: replayed.carrier,
|
|
96
|
+
targetRoot: admission.business.targetRoot,
|
|
97
|
+
};
|
|
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
|
+
await persistRenewResponse(context.journal, value, input.persistResponse);
|
|
106
|
+
return { kind: "committed", commit: replayed, entryKind: "renew", value };
|
|
87
107
|
}
|
|
88
|
-
|
|
89
|
-
|
|
108
|
+
if (prepared.build?.status === "conflict") {
|
|
109
|
+
const conflict = prepared.build;
|
|
110
|
+
context.beginEffects();
|
|
111
|
+
context.journal.record({
|
|
112
|
+
effect: "renew-conflict-scratch",
|
|
113
|
+
idempotencyKey: `${input.contractId}:${admission.ledgerHead}:renew-conflict-scratch:${conflict.scratchName}`,
|
|
114
|
+
status: "failed",
|
|
115
|
+
cause: {
|
|
116
|
+
code: "CLOSE_MERGE_CONFLICT",
|
|
117
|
+
message: `renew conflict for ${input.contractId}`,
|
|
118
|
+
facts: {
|
|
119
|
+
kind: "renew_conflict_scratch",
|
|
120
|
+
contractId: input.contractId,
|
|
121
|
+
scratchName: conflict.scratchName,
|
|
122
|
+
scratchPath: conflict.scratchPath,
|
|
123
|
+
observedLedgerHead: admission.ledgerHead,
|
|
124
|
+
deliveryRef: admission.business.deliveryRef,
|
|
125
|
+
targetRef: admission.business.targetRef,
|
|
126
|
+
oldBase: admission.business.oldBase,
|
|
127
|
+
oldHead: admission.business.oldHead,
|
|
128
|
+
newBase: admission.business.newBase,
|
|
129
|
+
effectiveScope: admission.business.effectiveScope,
|
|
130
|
+
conflictCommit: conflict.conflictCommit,
|
|
131
|
+
conflictPaths: [...conflict.conflictPaths],
|
|
132
|
+
nextAction: "continue-or-abort",
|
|
133
|
+
authoritativeRefsChanged: false,
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
return { kind: "precommit-effects-only", authoritativeHead: admission.ledgerHead };
|
|
90
138
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
139
|
+
if (admission.business.oldBase === admission.business.newBase) {
|
|
140
|
+
if (!admission.business.amendment) {
|
|
141
|
+
const transactionObjectId = admission.replay.state === "verb-committed"
|
|
142
|
+
? admission.replay.transaction.transactionObjectId
|
|
143
|
+
: admission.ledgerHead;
|
|
144
|
+
const replayed = await context.replayCommitted({ transactionObjectId });
|
|
145
|
+
const accepted = replayed.transaction.entries.at(-1);
|
|
146
|
+
if (!accepted)
|
|
147
|
+
throw new FlowError("INTERNAL_STATE", `transaction ${transactionObjectId} has no accepted lifecycle entry`);
|
|
148
|
+
const value = {
|
|
149
|
+
...committedLifecycleFacts({ head: transactionObjectId, entryKind: accepted.kind, contractId: input.contractId, carrier: replayed.carrier }),
|
|
150
|
+
contractId: input.contractId,
|
|
151
|
+
oldBase: admission.business.oldBase,
|
|
152
|
+
newBase: admission.business.newBase,
|
|
153
|
+
oldHead: admission.business.oldHead,
|
|
154
|
+
newHead: admission.business.oldHead,
|
|
155
|
+
ledgerHead: transactionObjectId,
|
|
156
|
+
alreadyFresh: true,
|
|
157
|
+
sealOid: null,
|
|
158
|
+
renewOid: null,
|
|
159
|
+
sealed: null,
|
|
160
|
+
carrier: replayed.carrier,
|
|
161
|
+
targetRoot: admission.business.targetRoot,
|
|
162
|
+
};
|
|
163
|
+
await persistRenewResponse(context.journal, value, input.persistResponse);
|
|
164
|
+
return { kind: "committed", commit: replayed, entryKind: accepted.kind, value };
|
|
165
|
+
}
|
|
166
|
+
const published = await context.publishAuthoritative({
|
|
167
|
+
verb: "renew",
|
|
168
|
+
admittedLedgerHead: admission.ledgerHead,
|
|
169
|
+
acceptedLifecycleTail: context.session.lifecycle.history.at(-1).objectId,
|
|
170
|
+
entries: [admission.business.amendment],
|
|
171
|
+
});
|
|
172
|
+
if (published.status === "refused-before-effects")
|
|
173
|
+
return { kind: "refused-before-effects", authoritativeHead: admission.ledgerHead, refusal: { code: "CONCURRENT_MODIFICATION", message: "repository ledger changed before scope amendment" } };
|
|
174
|
+
if (published.status === "indeterminate")
|
|
175
|
+
return { kind: "precommit-effects-only", authoritativeHead: admission.ledgerHead };
|
|
176
|
+
const value = {
|
|
177
|
+
...committedLifecycleFacts({ head: published.head, entryKind: "amend", contractId: input.contractId, carrier: published.carrier }),
|
|
178
|
+
contractId: input.contractId,
|
|
179
|
+
oldBase: admission.business.oldBase,
|
|
180
|
+
newBase: admission.business.newBase,
|
|
181
|
+
oldHead: admission.business.oldHead,
|
|
182
|
+
newHead: admission.business.oldHead,
|
|
183
|
+
ledgerHead: published.head,
|
|
184
|
+
alreadyFresh: true,
|
|
185
|
+
sealOid: null,
|
|
186
|
+
renewOid: null,
|
|
187
|
+
sealed: null,
|
|
188
|
+
carrier: published.carrier,
|
|
189
|
+
targetRoot: admission.business.targetRoot,
|
|
190
|
+
};
|
|
191
|
+
await persistRenewResponse(context.journal, value, input.persistResponse);
|
|
192
|
+
return { kind: "committed", commit: published, entryKind: "amend", value };
|
|
96
193
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
194
|
+
if (!prepared.entry || prepared.build?.status !== "clean" || !prepared.capture)
|
|
195
|
+
throw new FlowError("INTERNAL_STATE", "renew detached build is incomplete");
|
|
196
|
+
const entries = prepared.prefix.length === 0
|
|
197
|
+
? [prepared.entry]
|
|
198
|
+
: prepared.prefix[0].kind === "seal"
|
|
199
|
+
? [prepared.prefix[0], prepared.entry]
|
|
200
|
+
: prepared.prefix.length === 1
|
|
201
|
+
? [prepared.entry]
|
|
202
|
+
: [prepared.prefix[0], prepared.prefix[1], prepared.entry];
|
|
203
|
+
const published = await context.publishAuthoritative({
|
|
204
|
+
verb: "renew",
|
|
205
|
+
admittedLedgerHead: admission.ledgerHead,
|
|
206
|
+
acceptedLifecycleTail: context.session.lifecycle.history.at(-1).objectId,
|
|
207
|
+
entries,
|
|
208
|
+
delivery: {
|
|
209
|
+
ref: admission.business.deliveryRef,
|
|
210
|
+
expectedOid: prepared.entry.data.oldHead,
|
|
111
211
|
},
|
|
112
212
|
});
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
facts: { kind: "renew_coordinate_drift", commission: input.contractId, oldBase: input.oldBase, oldHead: input.oldHead, fence, targetHead: input.targetHead },
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
if (branchBase === input.oldBase && fenceBase === fence)
|
|
156
|
-
return;
|
|
157
|
-
throw new FlowError("RENEW_COORDINATE_DRIFT", `renew coordinates for ${input.contractId} drifted from the ledger before scope audit`, {
|
|
158
|
-
facts: { kind: "renew_coordinate_drift", commission: input.contractId, oldBase: input.oldBase, oldHead: input.oldHead, fence, targetHead: input.targetHead },
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
async function finishRenew(input) {
|
|
162
|
-
const capture = input.capture === undefined
|
|
163
|
-
? await captureSeal(input.repositoryRoot, input.entries, input.oldHead)
|
|
164
|
-
: input.capture;
|
|
165
|
-
const settlement = await appendRenewSettlement({
|
|
166
|
-
cwd: input.repositoryRoot,
|
|
167
|
-
contractId: input.contractId,
|
|
168
|
-
expectedLedgerHead: input.ledgerHead,
|
|
169
|
-
capture,
|
|
170
|
-
oldBase: input.oldBase,
|
|
171
|
-
newBase: input.newBase,
|
|
172
|
-
oldHead: input.oldHead,
|
|
173
|
-
newHead: input.newHead,
|
|
174
|
-
actor: input.actor,
|
|
175
|
-
nowMs: input.nowMs,
|
|
176
|
-
});
|
|
177
|
-
await input.afterLedgerCas?.();
|
|
178
|
-
await refreshContractView({
|
|
179
|
-
ledgerCwd: input.repositoryRoot,
|
|
180
|
-
renderCwd: input.rewriteCwd,
|
|
181
|
-
contractId: input.contractId,
|
|
213
|
+
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" } };
|
|
215
|
+
if (published.status === "indeterminate")
|
|
216
|
+
return { kind: "precommit-effects-only", authoritativeHead: admission.ledgerHead };
|
|
217
|
+
const sealIndex = published.transaction.entries.findIndex((entry) => entry.kind === "seal");
|
|
218
|
+
const renewIndex = published.transaction.entries.findIndex((entry) => entry.kind === "renew");
|
|
219
|
+
const sealOid = sealIndex < 0 ? null : published.transaction.entryObjectIds[sealIndex];
|
|
220
|
+
const renewOid = renewIndex < 0 ? null : published.transaction.entryObjectIds[renewIndex];
|
|
221
|
+
const sealed = {
|
|
222
|
+
...prepared.capture,
|
|
223
|
+
newFence: prepared.build.newHead,
|
|
224
|
+
sealedBy: "renew",
|
|
225
|
+
ledgerHead: sealOid,
|
|
226
|
+
};
|
|
227
|
+
const value = {
|
|
228
|
+
...committedLifecycleFacts({ head: published.head, entryKind: "renew", contractId: input.contractId, carrier: published.carrier }),
|
|
229
|
+
contractId: input.contractId,
|
|
230
|
+
oldBase: admission.business.oldBase,
|
|
231
|
+
newBase: admission.business.newBase,
|
|
232
|
+
oldHead: admission.business.oldHead,
|
|
233
|
+
newHead: prepared.build.newHead,
|
|
234
|
+
ledgerHead: published.head,
|
|
235
|
+
alreadyFresh: false,
|
|
236
|
+
sealOid,
|
|
237
|
+
renewOid,
|
|
238
|
+
sealed,
|
|
239
|
+
carrier: published.carrier,
|
|
240
|
+
targetRoot: admission.business.targetRoot,
|
|
241
|
+
};
|
|
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
|
+
await persistRenewResponse(context.journal, value, input.persistResponse);
|
|
250
|
+
return { kind: "committed", commit: published, entryKind: "renew", value };
|
|
251
|
+
},
|
|
182
252
|
});
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
contractId: input.contractId
|
|
187
|
-
|
|
188
|
-
newBase: input.newBase,
|
|
189
|
-
oldHead: input.oldHead,
|
|
190
|
-
newHead: input.newHead,
|
|
191
|
-
ledgerHead: settlement.ledgerHead,
|
|
192
|
-
alreadyFresh: false,
|
|
193
|
-
sealed: settlement.sealed,
|
|
194
|
-
recoveryCleanup,
|
|
195
|
-
};
|
|
253
|
+
if (execution.kind === "noncommitted")
|
|
254
|
+
return execution.outcome;
|
|
255
|
+
if (execution.kind === "contained")
|
|
256
|
+
return { ...execution.outcome, contractId: input.contractId };
|
|
257
|
+
return { ...execution.payload, ...execution.outcome };
|
|
196
258
|
}
|
|
197
259
|
export async function renewContract(input) {
|
|
198
|
-
|
|
199
|
-
const worktreePath = await findContractWorktreePath(repositoryRoot, input.contractId);
|
|
200
|
-
const rewriteCwd = worktreePath ?? input.cwd;
|
|
201
|
-
await assertNoRenewRecoverySession(repositoryRoot, input.contractId);
|
|
202
|
-
if (input.resolveConflicts && !worktreePath) {
|
|
203
|
-
throw new FlowError("INTERNAL_STATE", `renew --resolve requires ${input.contractId}'s registered contract worktree`);
|
|
204
|
-
}
|
|
205
|
-
await assertCleanTrackedWorkingTree(rewriteCwd);
|
|
206
|
-
const ledger = await readLedger(input.cwd, input.contractId);
|
|
207
|
-
if (!ledger) {
|
|
208
|
-
throw new FlowError("INTERNAL_STATE", `contract ${input.contractId} not found or not active`);
|
|
209
|
-
}
|
|
210
|
-
assertOperable(input.contractId, ledger.entries);
|
|
211
|
-
const commission = commissionTarget(ledger.entries);
|
|
212
|
-
if (!commission) {
|
|
213
|
-
throw new FlowError("INTERNAL_STATE", `cannot renew ${input.contractId}: missing commission target`);
|
|
214
|
-
}
|
|
215
|
-
const targetHead = await resolveCommitOrNull(input.cwd, commission.ref);
|
|
216
|
-
if (!targetHead) {
|
|
217
|
-
throw new FlowError("INTERNAL_STATE", `cannot renew ${input.contractId}: commission target ${commission.ref} does not resolve`);
|
|
218
|
-
}
|
|
219
|
-
const oldBase = latestBase(ledger.entries);
|
|
220
|
-
if (!oldBase)
|
|
221
|
-
throw new FlowError("INTERNAL_STATE", "ledger must contain bind entry");
|
|
222
|
-
const branch = `keiyaku/${input.contractId}`;
|
|
223
|
-
const oldHead = await resolveCommit(input.cwd, branch);
|
|
224
|
-
await assertRenewCoordinates({
|
|
225
|
-
cwd: repositoryRoot,
|
|
226
|
-
contractId: input.contractId,
|
|
227
|
-
oldBase,
|
|
228
|
-
oldHead,
|
|
229
|
-
targetHead,
|
|
230
|
-
entries: ledger.entries,
|
|
231
|
-
});
|
|
232
|
-
if (getMaxArc(ledger.entries) > 0 && !findOpenArc(ledger.entries)) {
|
|
233
|
-
const loose = await receiptsAfterFence(input.cwd, ledger.entries, oldHead);
|
|
234
|
-
if (loose.length > 0)
|
|
235
|
-
throw looseCommitError(input.contractId, loose);
|
|
236
|
-
}
|
|
237
|
-
if (oldBase !== targetHead) {
|
|
238
|
-
const deliveryWorktree = worktreePath ?? repositoryRoot;
|
|
239
|
-
await assertNoInFlightProjection(repositoryRoot, input.contractId, deliveryWorktree, input.nowMs);
|
|
240
|
-
}
|
|
241
|
-
await appendScopeTerms({
|
|
242
|
-
cwd: repositoryRoot,
|
|
243
|
-
renderCwd: rewriteCwd,
|
|
244
|
-
contractId: input.contractId,
|
|
245
|
-
patterns: input.appendScope,
|
|
246
|
-
actor: input.actor,
|
|
247
|
-
nowMs: input.nowMs,
|
|
248
|
-
});
|
|
249
|
-
const currentLedger = input.appendScope?.length ? await readLedger(repositoryRoot, input.contractId) : ledger;
|
|
250
|
-
if (!currentLedger)
|
|
251
|
-
throw new FlowError("INTERNAL_STATE", `contract ${input.contractId} disappeared while appending scope`);
|
|
252
|
-
const capture = await captureSeal(input.cwd, currentLedger.entries, oldHead);
|
|
253
|
-
if (oldBase === targetHead) {
|
|
254
|
-
return {
|
|
255
|
-
contractId: input.contractId,
|
|
256
|
-
oldBase,
|
|
257
|
-
newBase: targetHead,
|
|
258
|
-
oldHead,
|
|
259
|
-
newHead: oldHead,
|
|
260
|
-
ledgerHead: currentLedger.head,
|
|
261
|
-
alreadyFresh: true,
|
|
262
|
-
sealed: null,
|
|
263
|
-
recoveryCleanup: null,
|
|
264
|
-
};
|
|
265
|
-
}
|
|
266
|
-
const recoveryRef = renewRecoveryRef(input.contractId, oldHead);
|
|
267
|
-
await createRecoveryRef(input.cwd, recoveryRef, oldHead);
|
|
268
|
-
if (input.resolveConflicts) {
|
|
269
|
-
await writeRenewRecoverySession(repositoryRoot, {
|
|
270
|
-
commission: input.contractId,
|
|
271
|
-
oldBase,
|
|
272
|
-
oldHead,
|
|
273
|
-
newBase: targetHead,
|
|
274
|
-
recoveryRef,
|
|
275
|
-
});
|
|
276
|
-
}
|
|
277
|
-
try {
|
|
278
|
-
await rewriteRenewBranch({
|
|
279
|
-
cwd: rewriteCwd,
|
|
280
|
-
contractId: input.contractId,
|
|
281
|
-
oldHead,
|
|
282
|
-
recoveryRef,
|
|
283
|
-
newBase: targetHead,
|
|
284
|
-
oldBase,
|
|
285
|
-
branch: worktreePath ? undefined : branch,
|
|
286
|
-
preserveConflict: input.resolveConflicts,
|
|
287
|
-
});
|
|
288
|
-
}
|
|
289
|
-
catch (error) {
|
|
290
|
-
if (input.resolveConflicts && !(error instanceof FlowError && error.code === "RENEW_SESSION_OPEN")) {
|
|
291
|
-
await removeRenewRecoverySession(repositoryRoot, input.contractId);
|
|
292
|
-
}
|
|
293
|
-
throw error;
|
|
294
|
-
}
|
|
295
|
-
const newHead = await resolveCommit(input.cwd, branch);
|
|
296
|
-
return await finishRenew({
|
|
297
|
-
repositoryRoot,
|
|
298
|
-
rewriteCwd,
|
|
299
|
-
ledgerHead: currentLedger.head,
|
|
300
|
-
entries: currentLedger.entries,
|
|
301
|
-
contractId: input.contractId,
|
|
302
|
-
oldBase,
|
|
303
|
-
oldHead,
|
|
304
|
-
newBase: targetHead,
|
|
305
|
-
newHead,
|
|
306
|
-
recoveryRef,
|
|
307
|
-
actor: input.actor,
|
|
308
|
-
nowMs: input.nowMs,
|
|
309
|
-
capture,
|
|
310
|
-
afterLedgerCas: input.afterLedgerCas,
|
|
311
|
-
});
|
|
260
|
+
return executeRenew(input);
|
|
312
261
|
}
|
|
313
|
-
async function
|
|
314
|
-
const
|
|
262
|
+
export async function continueRenewContract(input) {
|
|
263
|
+
const scratchName = renewScratchName(input.contractId);
|
|
264
|
+
let scratch;
|
|
315
265
|
try {
|
|
316
|
-
|
|
266
|
+
scratch = await readDetachedRenewScratch({ cwd: input.cwd, scratchName });
|
|
317
267
|
}
|
|
318
268
|
catch (error) {
|
|
319
|
-
throw
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
async function assertRecoveryFacts(repositoryRoot, contractId, session) {
|
|
323
|
-
if (session.commission !== contractId) {
|
|
324
|
-
throw new FlowError("INTERNAL_STATE", `renew recovery session belongs to ${session.commission}, not ${contractId}`);
|
|
325
|
-
}
|
|
326
|
-
const rewriteCwd = await findRegisteredContractWorktreePath(repositoryRoot, contractId);
|
|
327
|
-
if (!rewriteCwd)
|
|
328
|
-
throw new FlowError("INTERNAL_STATE", `registered contract worktree disappeared for ${contractId}`);
|
|
329
|
-
const ledger = await readLedger(repositoryRoot, contractId);
|
|
330
|
-
if (!ledger)
|
|
331
|
-
throw new FlowError("INTERNAL_STATE", `contract ${contractId} not found or not active`);
|
|
332
|
-
const savedHead = await resolveCommit(repositoryRoot, session.recoveryRef);
|
|
333
|
-
if (savedHead !== session.oldHead || latestBase(ledger.entries) !== session.oldBase) {
|
|
334
|
-
throw new FlowError("RENEW_SESSION_OPEN", `renew recovery session facts no longer match the owned rewrite for ${contractId}`, {
|
|
335
|
-
facts: { kind: "renew_session", commission: contractId, nextAction: "abort" },
|
|
336
|
-
});
|
|
269
|
+
throw new FlowError("RENEW_SCRATCH_NOT_FOUND", `no renew conflict scratch is open for ${input.contractId}`, { cause: error });
|
|
337
270
|
}
|
|
338
|
-
return {
|
|
339
|
-
}
|
|
340
|
-
async function resultFromSpentSession(repositoryRoot, spent) {
|
|
341
|
-
const seal = spent.seal;
|
|
342
|
-
const sealed = seal ? {
|
|
343
|
-
...seal.data,
|
|
344
|
-
scopeViolationEvidence: [],
|
|
345
|
-
ledgerHead: await resolveCommit(repositoryRoot, `${spent.ledger.head}^`),
|
|
346
|
-
} : null;
|
|
347
|
-
return {
|
|
348
|
-
contractId: spent.session.commission,
|
|
349
|
-
...spent.renew.data,
|
|
350
|
-
ledgerHead: spent.ledger.head,
|
|
351
|
-
alreadyFresh: false,
|
|
352
|
-
sealed,
|
|
353
|
-
recoveryCleanup: spent.recoveryCleanup,
|
|
354
|
-
};
|
|
355
|
-
}
|
|
356
|
-
export async function continueRenewContract(input) {
|
|
357
|
-
const repositoryRoot = await stableRepoRoot(input.cwd);
|
|
358
|
-
const sessionState = await convergeRenewRecoverySession(repositoryRoot, input.contractId);
|
|
359
|
-
if (sessionState.state === "spent")
|
|
360
|
-
return await resultFromSpentSession(repositoryRoot, sessionState);
|
|
361
|
-
if (sessionState.state === "none") {
|
|
362
|
-
throw new FlowError("RENEW_SESSION_NOT_FOUND", `no renew recovery session is open for ${input.contractId}`);
|
|
363
|
-
}
|
|
364
|
-
const session = sessionState.session;
|
|
365
|
-
const { ledger, rewriteCwd } = await assertRecoveryFacts(repositoryRoot, input.contractId, session);
|
|
366
|
-
const delivery = await revListRange(repositoryRoot, `${session.oldBase}..${session.oldHead}`);
|
|
367
|
-
if (await hasActiveRebase(rewriteCwd)) {
|
|
368
|
-
const stoppedAt = await resolveCommitOrNull(rewriteCwd, "REBASE_HEAD");
|
|
369
|
-
if (!stoppedAt) {
|
|
370
|
-
throw new FlowError("RENEW_SESSION_OPEN", `owned rebase state has no stopped receipt for ${input.contractId}`, {
|
|
371
|
-
facts: { kind: "renew_session", commission: input.contractId, nextAction: "abort" },
|
|
372
|
-
});
|
|
373
|
-
}
|
|
374
|
-
const stoppedIndex = delivery.indexOf(stoppedAt);
|
|
375
|
-
const rewrittenPrefix = await revListRange(rewriteCwd, `${session.newBase}..HEAD`);
|
|
376
|
-
if (stoppedIndex < 0 || rewrittenPrefix.length !== stoppedIndex) {
|
|
377
|
-
throw new FlowError("RENEW_SESSION_OPEN", `loose commits do not belong to the owned renew rebase for ${input.contractId}`, {
|
|
378
|
-
facts: { kind: "renew_session", commission: input.contractId, nextAction: "continue-or-abort" },
|
|
379
|
-
});
|
|
380
|
-
}
|
|
381
|
-
const continued = await runGitProcess(rewriteCwd, ["rebase", "--continue"], {
|
|
382
|
-
env: { GIT_EDITOR: "true" },
|
|
383
|
-
});
|
|
384
|
-
if (continued.status !== 0) {
|
|
385
|
-
const error = continued.error ?? { message: "git rebase --continue failed", stderr: continued.stderr, stdout: continued.stdout };
|
|
386
|
-
if (errorContainsAnyPattern(error, ["CONFLICT", "could not apply", "Resolve all conflicts manually"])) {
|
|
387
|
-
throw new FlowError("RENEW_SESSION_OPEN", `renew recovery session is open for ${input.contractId}`, {
|
|
388
|
-
cause: error,
|
|
389
|
-
facts: { kind: "renew_session", commission: input.contractId, nextAction: "resolve-conflicts" },
|
|
390
|
-
});
|
|
391
|
-
}
|
|
392
|
-
throw wrapGitError("rebase --continue", error, rewriteCwd);
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
const newHead = await resolveCommit(repositoryRoot, `keiyaku/${input.contractId}`);
|
|
396
|
-
const rewritten = await revListRange(repositoryRoot, `${session.newBase}..${newHead}`);
|
|
397
|
-
if (await mergeBase(repositoryRoot, session.newBase, newHead) !== session.newBase || rewritten.length !== delivery.length) {
|
|
398
|
-
throw new FlowError("RENEW_SESSION_OPEN", `rewritten delivery no longer corresponds to the saved renew receipts for ${input.contractId}`, {
|
|
399
|
-
facts: { kind: "renew_session", commission: input.contractId, nextAction: "abort" },
|
|
400
|
-
});
|
|
401
|
-
}
|
|
402
|
-
await appendScopeTerms({
|
|
403
|
-
cwd: repositoryRoot,
|
|
404
|
-
renderCwd: rewriteCwd,
|
|
405
|
-
contractId: input.contractId,
|
|
406
|
-
patterns: input.appendScope,
|
|
407
|
-
actor: input.actor,
|
|
408
|
-
nowMs: input.nowMs,
|
|
409
|
-
});
|
|
410
|
-
const currentLedger = input.appendScope?.length ? await readLedger(repositoryRoot, input.contractId) : ledger;
|
|
411
|
-
if (!currentLedger)
|
|
412
|
-
throw new FlowError("INTERNAL_STATE", `contract ${input.contractId} disappeared while appending scope`);
|
|
413
|
-
const capture = await captureRenewSeal({
|
|
414
|
-
cwd: repositoryRoot,
|
|
415
|
-
entries: currentLedger.entries,
|
|
416
|
-
receiptHead: session.oldHead,
|
|
417
|
-
contentBase: session.newBase,
|
|
418
|
-
contentHead: newHead,
|
|
419
|
-
});
|
|
420
|
-
return await finishRenew({
|
|
421
|
-
repositoryRoot,
|
|
422
|
-
rewriteCwd,
|
|
423
|
-
ledgerHead: currentLedger.head,
|
|
424
|
-
entries: currentLedger.entries,
|
|
425
|
-
contractId: input.contractId,
|
|
426
|
-
oldBase: session.oldBase,
|
|
427
|
-
oldHead: session.oldHead,
|
|
428
|
-
newBase: session.newBase,
|
|
429
|
-
newHead,
|
|
430
|
-
recoveryRef: session.recoveryRef,
|
|
431
|
-
actor: input.actor,
|
|
432
|
-
nowMs: input.nowMs,
|
|
433
|
-
capture,
|
|
434
|
-
afterLedgerCas: input.afterLedgerCas,
|
|
435
|
-
});
|
|
271
|
+
return executeRenew(input, { scratch, resolution: input.resolution ?? { kind: "scratch-files" } });
|
|
436
272
|
}
|
|
437
273
|
export async function abortRenewContract(input) {
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
}
|
|
443
|
-
const session = sessionState.session;
|
|
444
|
-
const { rewriteCwd } = await assertRecoveryFacts(repositoryRoot, input.contractId, session);
|
|
445
|
-
const git = createGit(rewriteCwd);
|
|
446
|
-
if (await hasActiveRebase(rewriteCwd)) {
|
|
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);
|
|
277
|
+
let scratch;
|
|
447
278
|
try {
|
|
448
|
-
await
|
|
279
|
+
scratch = await readDetachedRenewScratch({ cwd: input.cwd, scratchName });
|
|
449
280
|
}
|
|
450
281
|
catch (error) {
|
|
451
|
-
throw
|
|
282
|
+
throw new FlowError("RENEW_SCRATCH_NOT_FOUND", `no renew conflict scratch is open for ${input.contractId}`, { cause: error });
|
|
452
283
|
}
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
try {
|
|
471
|
-
await git.raw(["reset", "--hard", session.oldHead]);
|
|
472
|
-
}
|
|
473
|
-
catch (error) {
|
|
474
|
-
throw wrapGitError(`reset --hard ${session.recoveryRef}`, error, rewriteCwd);
|
|
475
|
-
}
|
|
476
|
-
await assertCleanTrackedWorkingTree(rewriteCwd);
|
|
477
|
-
await removeRenewRecoverySession(repositoryRoot, input.contractId);
|
|
478
|
-
const recoveryCleanup = await deleteRecoveryRef(repositoryRoot, session.recoveryRef, session.oldHead);
|
|
479
|
-
return { contractId: input.contractId, oldHead: session.oldHead, recoveryCleanup };
|
|
284
|
+
checkCancellation(input.signal);
|
|
285
|
+
const cleanup = await abortDetachedRenewChain({ cwd: input.cwd, scratchName });
|
|
286
|
+
return {
|
|
287
|
+
...derivedCleanupFacts({
|
|
288
|
+
contractId: input.contractId,
|
|
289
|
+
authoritativeHead: session.lifecycle.observedLedgerHead,
|
|
290
|
+
effect: "renew-conflict-scratch-cleanup",
|
|
291
|
+
receipt: { status: cleanup.status, scratchName, oldHead: scratch.oldHead },
|
|
292
|
+
}),
|
|
293
|
+
contractId: input.contractId,
|
|
294
|
+
oldHead: scratch.oldHead,
|
|
295
|
+
scratchName,
|
|
296
|
+
carrier: session.initialCarrier,
|
|
297
|
+
};
|
|
298
|
+
});
|
|
480
299
|
}
|
|
481
300
|
export async function renewKeiyaku(input) {
|
|
482
301
|
await requireGitRepository(input.cwd, "renew");
|
|
483
302
|
const address = input.address ?? await resolveContractAddress(input.cwd, input.contractId, input.contractAddressSource);
|
|
484
|
-
return
|
|
485
|
-
const
|
|
486
|
-
|
|
487
|
-
cwd: input.cwd,
|
|
488
|
-
contractId,
|
|
489
|
-
actor: ACTOR_IDENTITY,
|
|
490
|
-
nowMs: Date.now(),
|
|
491
|
-
appendScope: input.appendScope,
|
|
492
|
-
resolveConflicts: input.resolveConflicts,
|
|
493
|
-
});
|
|
494
|
-
return await buildRenewOutcome(input.cwd, address, renewed);
|
|
303
|
+
return withResolvedAddress(address, async () => {
|
|
304
|
+
const result = await renewContract({ cwd: input.cwd, contractId: address.binding.commissionId, actor: ACTOR_IDENTITY, nowMs: Date.now(), appendScope: input.appendScope, signal: input.signal, persistResponse: renewResponsePersistence(input.cwd, address) });
|
|
305
|
+
return isCompleteLifecycleOutcome(result) ? buildRenewOutcome(address, result) : result;
|
|
495
306
|
});
|
|
496
307
|
}
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
: [];
|
|
507
|
-
let responsePath;
|
|
508
|
-
let artifactId;
|
|
509
|
-
try {
|
|
510
|
-
const persisted = await persistResponseHistory({
|
|
511
|
-
cwd,
|
|
512
|
-
tool: "renew",
|
|
513
|
-
provenance: { authority: { kind: "repository" }, binding: address.binding, evidenceCwd: cwd },
|
|
514
|
-
title: contractId,
|
|
515
|
-
oldBase: renewed.oldBase,
|
|
516
|
-
newBase: renewed.newBase,
|
|
517
|
-
oldHead: renewed.oldHead,
|
|
518
|
-
newHead: renewed.newHead,
|
|
519
|
-
ledgerCommit: renewed.ledgerHead,
|
|
520
|
-
summary,
|
|
521
|
-
emptyDiffLabel: "nothing sealed",
|
|
522
|
-
...(renewed.sealed ? { sealed: renewed.sealed } : {}),
|
|
523
|
-
});
|
|
524
|
-
responsePath = persisted.responsePath;
|
|
525
|
-
artifactId = persisted.artifactId;
|
|
526
|
-
}
|
|
527
|
-
catch {
|
|
528
|
-
responsePath = undefined;
|
|
529
|
-
}
|
|
308
|
+
function renewResponsePersistence(cwd, address) {
|
|
309
|
+
return async (facts) => {
|
|
310
|
+
const contractId = address.binding.commissionId;
|
|
311
|
+
const summary = facts.alreadyFresh ? `Contract ${contractId} is already fresh; nothing touched.` : `Contract ${contractId} renewed onto ${facts.newBase}.`;
|
|
312
|
+
return persistResponseHistory({ cwd, tool: "renew", provenance: { authority: { kind: "repository" }, binding: address.binding, evidenceCwd: cwd }, title: contractId, oldBase: facts.oldBase, newBase: facts.newBase, oldHead: facts.oldHead, newHead: facts.newHead, ledgerCommit: facts.ledgerHead, summary, emptyDiffLabel: "nothing sealed", ...(facts.sealed ? { sealed: facts.sealed } : {}) });
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
async function buildRenewOutcome(address, renewed) {
|
|
316
|
+
const { targetRoot, ...facts } = renewed;
|
|
530
317
|
return {
|
|
531
318
|
status: "success",
|
|
532
|
-
...
|
|
533
|
-
currentBranch,
|
|
534
|
-
baseBranch,
|
|
535
|
-
// CLI/render synthesizes public face bytes from sealed typed facts.
|
|
319
|
+
...facts,
|
|
320
|
+
currentBranch: await getCurrentBranch(targetRoot),
|
|
321
|
+
baseBranch: address.target.branch.slice("refs/heads/".length),
|
|
536
322
|
diff: "",
|
|
537
323
|
sealed: renewed.sealed ?? undefined,
|
|
538
|
-
summary
|
|
539
|
-
...(cleanupWarnings.length > 0 ? { meta: { warnings: cleanupWarnings } } : {}),
|
|
540
|
-
responsePath,
|
|
541
|
-
artifactId,
|
|
324
|
+
summary: renewed.alreadyFresh ? `Contract ${renewed.contractId} is already fresh; nothing touched.` : `Contract ${renewed.contractId} renewed onto ${renewed.newBase}.`,
|
|
542
325
|
address,
|
|
543
326
|
};
|
|
544
327
|
}
|
|
545
328
|
export async function continueRenewKeiyaku(input) {
|
|
546
329
|
await requireGitRepository(input.cwd, "renew --continue");
|
|
547
|
-
return
|
|
548
|
-
cwd: input.cwd,
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
nowMs: Date.now(),
|
|
552
|
-
appendScope: input.appendScope,
|
|
553
|
-
})));
|
|
330
|
+
return withResolvedAddress(input.address, async () => {
|
|
331
|
+
const result = await continueRenewContract({ cwd: input.cwd, contractId: input.address.binding.commissionId, actor: ACTOR_IDENTITY, nowMs: Date.now(), appendScope: input.appendScope, signal: input.signal, persistResponse: renewResponsePersistence(input.cwd, input.address) });
|
|
332
|
+
return isCompleteLifecycleOutcome(result) ? buildRenewOutcome(input.address, result) : result;
|
|
333
|
+
});
|
|
554
334
|
}
|
|
555
335
|
export async function abortRenewKeiyaku(input) {
|
|
556
336
|
await requireGitRepository(input.cwd, "renew --abort");
|
|
557
|
-
return
|
|
558
|
-
const result = await abortRenewContract({ cwd: input.cwd, contractId: input.address.binding.commissionId });
|
|
559
|
-
return {
|
|
560
|
-
status: "success",
|
|
561
|
-
...result,
|
|
562
|
-
address: input.address,
|
|
563
|
-
summary: `Renew recovery for ${result.contractId} aborted; delivery restored to ${result.oldHead}.`,
|
|
564
|
-
};
|
|
337
|
+
return withResolvedAddress(input.address, async () => {
|
|
338
|
+
const result = await abortRenewContract({ cwd: input.cwd, contractId: input.address.binding.commissionId, signal: input.signal });
|
|
339
|
+
return { status: "success", ...result, address: input.address, summary: `Renew conflict scratch for ${result.contractId} aborted; authoritative refs were untouched.` };
|
|
565
340
|
});
|
|
566
341
|
}
|