@astrosheep/keiyaku 2.9.12 → 2.9.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -18
- package/build/.tsbuildinfo +1 -1
- package/build/agents/call-terms.js +1 -0
- package/build/agents/providers/claude-agent-sdk/adapter.js +2 -2
- package/build/cli/commands/akuma/akuma/handler.js +2 -1
- package/build/cli/commands/akuma/akuma/meta.js +4 -3
- package/build/cli/commands/akuma/catalog.js +2 -0
- package/build/cli/commands/akuma/list/handler.js +1 -1
- package/build/cli/commands/akuma/list/meta-list.js +12 -0
- package/build/cli/commands/akuma/list/meta-ls.js +2 -2
- package/build/cli/commands/akuma.js +5 -0
- package/build/cli/commands/contract/amend/meta.js +4 -3
- package/build/cli/commands/contract/arc/meta.js +2 -0
- package/build/cli/commands/contract/audit/handler.js +28 -2
- package/build/cli/commands/contract/audit/meta.js +4 -3
- package/build/cli/commands/contract/petition/handler.js +2 -1
- package/build/cli/commands/contract/petition/meta.js +2 -2
- package/build/cli/commands/contract/renew/handler.js +44 -3
- package/build/cli/commands/contract/renew/meta.js +5 -4
- package/build/cli/commands/metadata.js +12 -9
- package/build/cli/commands/projection/call/handler.js +1 -1
- package/build/cli/commands/projection/call/meta.js +1 -1
- package/build/cli/commands/projection/catalog.js +2 -2
- package/build/cli/commands/projection/{revive → fork}/handler.js +10 -5
- package/build/cli/commands/projection/fork/meta.js +12 -0
- package/build/cli/commands/projection/history/handler.js +46 -2
- package/build/cli/commands/projection/history/meta.js +3 -3
- package/build/cli/commands/projection/kill/meta.js +2 -2
- package/build/cli/commands/projection/status/handler.js +29 -13
- package/build/cli/commands/projection/status/meta.js +14 -3
- package/build/cli/commands/projection/tell/handler.js +8 -4
- package/build/cli/commands/projection/tell/meta.js +1 -1
- package/build/cli/commands/projection/wait/handler.js +45 -20
- package/build/cli/commands/projection/wait/meta.js +1 -1
- package/build/cli/commands/shared.js +1 -1
- package/build/cli/commands/system/catalog.js +2 -0
- package/build/cli/commands/task/add/handler.js +28 -12
- package/build/cli/commands/task/add/meta.js +18 -6
- package/build/cli/commands/task/catalog.js +2 -0
- package/build/cli/commands/task/compose/handler.js +2 -2
- package/build/cli/commands/task/compose/meta.js +2 -2
- package/build/cli/commands/task/doctor/handler.js +2 -3
- package/build/cli/commands/task/doctor/meta.js +2 -2
- package/build/cli/commands/task/done/meta.js +2 -2
- package/build/cli/commands/task/drop/handler.js +7 -2
- package/build/cli/commands/task/drop/meta.js +6 -3
- package/build/cli/commands/task/hold/meta.js +2 -2
- package/build/cli/commands/task/log/meta.js +2 -2
- package/build/cli/commands/task/ls/meta.js +2 -2
- package/build/cli/commands/task/note/handler.js +6 -0
- package/build/cli/commands/task/note/meta.js +11 -0
- package/build/cli/commands/task/resume/meta.js +2 -2
- package/build/cli/commands/task/shared.js +29 -15
- package/build/cli/commands/task/show/meta.js +2 -2
- package/build/cli/commands/task/start/meta.js +2 -2
- package/build/cli/commands/task/stop/meta.js +2 -2
- package/build/cli/commands/task/update/meta.js +3 -2
- package/build/cli/commands/verification/handler.js +43 -0
- package/build/cli/commands/verification/meta.js +13 -0
- package/build/cli/completion.js +16 -13
- package/build/cli/flags.js +43 -11
- package/build/cli/help.js +29 -30
- package/build/cli/index.js +6 -5
- package/build/cli/parse-flags.js +39 -0
- package/build/cli/parse-metadata.js +5 -2
- package/build/cli/parse-selectors.js +18 -0
- package/build/cli/parse.js +21 -8
- package/build/cli/render/arc.js +12 -3
- package/build/cli/render/audit.js +2 -2
- package/build/cli/render/call.js +4 -4
- package/build/cli/render/kanshi.js +122 -85
- package/build/cli/render/misc.js +2 -2
- package/build/cli/render/petition.js +16 -1
- package/build/cli/render/projection-history.js +17 -4
- package/build/cli/render/shared.js +5 -5
- package/build/cli/render/status-indicator.js +47 -0
- package/build/cli/render/status.js +106 -76
- package/build/cli/render/success-response.js +20 -5
- package/build/cli/render/task.js +6 -5
- package/build/cli/render/tell.js +10 -4
- package/build/cli/render/tool-command-normalization.js +137 -0
- package/build/cli/render/tool-presentation.js +2 -1
- package/build/cli/render/verification.js +23 -0
- package/build/cli/render/wait.js +101 -33
- package/build/cli/types.js +2 -1
- package/build/config/akuma-loader.js +21 -1
- package/build/config/settings/knobs.js +7 -1
- package/build/config/settings/schema.js +10 -5
- package/build/core/amend.js +102 -17
- package/build/core/arc.js +4 -4
- package/build/core/audit/candidate.js +10 -18
- package/build/core/audit/coordinates.js +12 -17
- package/build/core/audit/evidence.js +6 -4
- package/build/core/audit/facade.js +3 -3
- package/build/core/audit/report.js +1 -1
- package/build/core/bind-reconciliation.js +42 -23
- package/build/core/bind.js +182 -81
- package/build/core/call/call.js +6 -7
- package/build/core/call/context.js +43 -30
- package/build/core/call/execution.js +21 -6
- package/build/core/call/prompt.js +11 -42
- package/build/core/contract-carrier-runtime.js +209 -105
- package/build/core/contract-carrier.js +156 -73
- package/build/core/contract-view.js +42 -5
- package/build/core/contract.js +2 -1
- package/build/core/derived-replay.js +168 -35
- package/build/core/draft.js +9 -5
- package/build/core/forfeit.js +1 -1
- package/build/core/lifecycle-history.js +13 -0
- package/build/core/lifecycle-recovery.js +32 -27
- package/build/core/lifecycle-runner.js +14 -14
- package/build/core/log.js +1 -1
- package/build/core/outcome-base.js +2 -2
- package/build/core/projection/akuma-name.js +43 -0
- package/build/core/projection/generation/database.js +141 -79
- package/build/core/projection/generation/ledger.js +91 -0
- package/build/core/projection/generation/model.js +18 -13
- package/build/core/projection/generation/projection-generation-continuation.js +1 -1
- package/build/core/projection/generation/projection-generation-doorbell.js +88 -0
- package/build/core/projection/generation/projection-generation-execution.js +24 -0
- package/build/core/projection/generation/projection-generation-identity.js +4 -0
- package/build/core/projection/generation/projection-generation-launcher.js +72 -138
- package/build/core/projection/generation/projection-generation-process.js +16 -15
- package/build/core/projection/generation/projection-generation-runner.js +125 -79
- package/build/core/projection/generation/projection-generation-runtime.js +10 -61
- package/build/core/projection/generation/protocol.js +68 -0
- package/build/core/projection/generation/store.js +17 -7
- package/build/core/projection/generation/transitions.js +14 -14
- package/build/core/projection/index.js +5 -5
- package/build/core/projection/projection-core.js +6 -2
- package/build/core/projection/projection-execution-observer.js +1 -1
- package/build/core/projection/projection-history.js +7 -7
- package/build/core/projection/projection-kill.js +15 -6
- package/build/core/projection/projection-life-observer.js +8 -8
- package/build/core/projection/projection-mint.js +6 -8
- package/build/core/projection/projection-status-observation.js +77 -8
- package/build/core/projection/projection-status.js +151 -4
- package/build/core/projection/projection-wait.js +46 -27
- package/build/core/projection/projection-wake.js +27 -89
- package/build/core/projection/tell/launch-store.js +1 -1
- package/build/core/projection/tell/store.js +1 -1
- package/build/core/renew-build.js +204 -74
- package/build/core/renew-plan.js +34 -9
- package/build/core/renew-rewrite.js +56 -15
- package/build/core/renew.js +39 -35
- package/build/core/repository-ledger/accepted-fold-read.js +6 -3
- package/build/core/repository-ledger/codec.js +11 -5
- package/build/core/repository-ledger/current-state-store.js +13 -16
- package/build/core/repository-ledger/fold-repository.js +5 -6
- package/build/core/repository-ledger/identity.js +60 -0
- package/build/core/repository-ledger/inventory.js +3 -3
- package/build/core/repository-ledger/read-model.js +54 -6
- package/build/core/repository-ledger/write-transaction.js +9 -4
- package/build/core/run-control/connection-bound-close.js +41 -0
- package/build/core/run-control/detached-lease-bootstrap.js +208 -0
- package/build/core/run-control/detached-process.js +14 -0
- package/build/core/run-control/index.js +6 -0
- package/build/core/{process-group.js → run-control/process-tree.js} +1 -1
- package/build/core/run-control/runner-lease.js +172 -0
- package/build/core/{sqlite-process-lifetime-lock.js → run-control/sqlite-process-lifetime-lock.js} +4 -24
- package/build/core/seal.js +44 -32
- package/build/core/settlement/claim-delivery.js +22 -9
- package/build/core/settlement/claim.js +8 -6
- package/build/core/settlement/petition-claim-gates.js +9 -8
- package/build/core/settlement/petition-head-guard.js +5 -6
- package/build/core/settlement/petition-preview.js +21 -5
- package/build/core/settlement/petition.js +51 -17
- package/build/core/settlement/settlement.js +20 -15
- package/build/core/settlement/verdict.js +2 -2
- package/build/core/settlement/verification.js +492 -364
- package/build/core/status/board.js +184 -75
- package/build/core/status/drift.js +8 -10
- package/build/core/task/board.js +1 -0
- package/build/core/task/commands.js +25 -0
- package/build/core/task/compose.js +3 -2
- package/build/core/task/document.js +102 -39
- package/build/core/task/index.js +3 -3
- package/build/core/task/settlement-git.js +1 -4
- package/build/core/task/settlement-policy.js +27 -14
- package/build/core/task/source-board.js +1 -0
- package/build/core/task/task-bind-preparation.js +59 -9
- package/build/core/task/task-contract.js +2 -2
- package/build/core/task/task-store-repository.js +1 -0
- package/build/core/task/task.js +2 -2
- package/build/core/transcripts.js +4 -4
- package/build/core/verification-declaration.js +95 -0
- package/build/core/worktree-bootstrap.js +1 -1
- package/build/core/worktree-path.js +7 -11
- package/build/flow-error.js +9 -1
- package/build/generated/version.js +2 -2
- package/build/git/branches.js +41 -70
- package/build/git/commits.js +92 -21
- package/build/git/core.js +9 -161
- package/build/git/diff/preview.js +4 -4
- package/build/git/diff/read.js +4 -4
- package/build/git/diff/structured.js +5 -5
- package/build/git/process.js +229 -0
- package/build/git/refs.js +30 -5
- package/build/git/staging.js +39 -115
- package/build/git/streaming-batch.js +9 -16
- package/build/git/worktree.js +104 -91
- package/build/index.js +7 -0
- package/package.json +1 -1
- package/skills/keiyaku-akuma/SKILL.md +21 -20
- package/skills/keiyaku-task/SKILL.md +38 -4
- package/skills/keiyaku-workflow/SKILL.md +51 -11
- package/build/cli/commands/projection/revive/meta.js +0 -12
- package/build/core/projection/projection-life-protocol.js +0 -115
- package/build/core/projection/projection-runner-lock.js +0 -211
- package/build/core/projection/tell/database.js +0 -127
- package/build/core/settlement/verification-coordination.js +0 -305
- package/build/core/settlement/verification-supervisor.js +0 -275
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { FlowError } from "../../flow-error.js";
|
|
2
|
-
import { createGit,
|
|
3
|
-
import { shortCommitHash } from "../../git/commits.js";
|
|
2
|
+
import { createGit, runGitProcess } from "../../git/core.js";
|
|
3
|
+
import { readCommitCoordinate, shortCommitHash } from "../../git/commits.js";
|
|
4
4
|
import { assertHonestClaimEvidence, buildHonestClaimMerge, } from "../repository-ledger/claim-evidence.js";
|
|
5
5
|
async function successfulGit(cwd, args, input) {
|
|
6
6
|
const result = await runGitProcess(cwd, args, input === undefined ? undefined : { input });
|
|
@@ -22,16 +22,29 @@ export async function buildClaimLanding(input) {
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
export async function resolveCommit(cwd, rev) {
|
|
25
|
-
|
|
25
|
+
const read = await readCommitCoordinate(cwd, rev);
|
|
26
|
+
if (read.status === "ok")
|
|
27
|
+
return read.coordinate.commit;
|
|
28
|
+
if (read.status === "failed") {
|
|
29
|
+
const detail = read.result.stderr.trim() || read.error.message || `exit ${String(read.result.status)}`;
|
|
30
|
+
throw new FlowError("INTERNAL_STATE", `git rev-parse ${rev} failed: ${detail}`, { cause: read.error });
|
|
31
|
+
}
|
|
32
|
+
if (read.status === "malformed") {
|
|
33
|
+
throw new FlowError("INTERNAL_STATE", `git rev-parse ${rev} failed: ${read.detail}`);
|
|
34
|
+
}
|
|
35
|
+
throw new FlowError("INTERNAL_STATE", `git rev-parse ${rev} failed: revision does not resolve to a commit`);
|
|
26
36
|
}
|
|
27
37
|
export async function resolveCommitOrNull(cwd, rev) {
|
|
28
|
-
const
|
|
29
|
-
if (
|
|
30
|
-
return
|
|
31
|
-
if (
|
|
38
|
+
const read = await readCommitCoordinate(cwd, rev);
|
|
39
|
+
if (read.status === "ok")
|
|
40
|
+
return read.coordinate.commit;
|
|
41
|
+
if (read.status === "missing" || read.status === "non-commit")
|
|
32
42
|
return null;
|
|
33
|
-
|
|
34
|
-
|
|
43
|
+
if (read.status === "malformed") {
|
|
44
|
+
throw new FlowError("INTERNAL_STATE", `git rev-parse ${rev} failed: ${read.detail}`);
|
|
45
|
+
}
|
|
46
|
+
const detail = read.result.stderr.trim() || read.error.message || `exit ${String(read.result.status)}`;
|
|
47
|
+
throw new FlowError("INTERNAL_STATE", `git rev-parse ${rev} failed: ${detail}`, { cause: read.error });
|
|
35
48
|
}
|
|
36
49
|
/** Prove parent order and tree identity independently of mutable refs. */
|
|
37
50
|
export async function proveClaimLanding(cwd, landing) {
|
|
@@ -179,7 +179,7 @@ export async function claimContract(input) {
|
|
|
179
179
|
: await context.publishAuthoritative({
|
|
180
180
|
verb: "claim",
|
|
181
181
|
admittedLedgerHead: admission.ledgerHead,
|
|
182
|
-
acceptedLifecycleTail: context.session.lifecycle.
|
|
182
|
+
acceptedLifecycleTail: context.session.lifecycle.contractHead,
|
|
183
183
|
entries: [{
|
|
184
184
|
v: 2,
|
|
185
185
|
kind: "claim",
|
|
@@ -221,11 +221,13 @@ export async function claimContract(input) {
|
|
|
221
221
|
},
|
|
222
222
|
});
|
|
223
223
|
await context.session.completeReplay("retire");
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
224
|
+
const warnings = ["task-board-projection", "target-worktree-projection"].flatMap((effect) => {
|
|
225
|
+
const row = context.journal.all().find((candidate) => candidate.effect === effect && candidate.status === "succeeded");
|
|
226
|
+
const facts = row?.status === "succeeded" ? row.receipt?.facts : undefined;
|
|
227
|
+
return isJsonObject(facts) && Array.isArray(facts.warnings)
|
|
228
|
+
? facts.warnings.filter((warning) => typeof warning === "string")
|
|
229
|
+
: [];
|
|
230
|
+
});
|
|
229
231
|
const cleanupRow = context.journal.all().find((row) => row.effect === "worktree-projection" && row.status === "succeeded");
|
|
230
232
|
const cleanupFacts = cleanupRow?.status === "succeeded" ? cleanupRow.receipt?.facts : undefined;
|
|
231
233
|
const deliveryRefRow = context.journal.all().find((row) => row.effect === "delivery-ref-projection" && row.status === "succeeded");
|
|
@@ -8,7 +8,7 @@ import { getDiffStats } from "../../git/diff/preview.js";
|
|
|
8
8
|
import { readDiff } from "../../git/diff/read.js";
|
|
9
9
|
import { getCurrentBranch } from "../../git/branches.js";
|
|
10
10
|
import { getLatestCommitHash } from "../../git/staging.js";
|
|
11
|
-
import {
|
|
11
|
+
import { requireCommitCoordinate } from "../../git/commits.js";
|
|
12
12
|
import { DIFF_PREVIEW_TEXT_MAX_CHARS } from "../../keiyaku.js";
|
|
13
13
|
import { FlowError } from "../../flow-error.js";
|
|
14
14
|
import { persistResponseHistory } from "../transcripts.js";
|
|
@@ -100,13 +100,14 @@ export function createOathGate() {
|
|
|
100
100
|
export function createCommandVerificationGate() {
|
|
101
101
|
return {
|
|
102
102
|
name: "command",
|
|
103
|
-
async evaluate({ cwd,
|
|
104
|
-
const repositoryRoot = coordinationRoot ?? cwd;
|
|
103
|
+
async evaluate({ cwd, repositoryRoot, verificationPlan, verificationCandidate, input }) {
|
|
105
104
|
for (const verification of verificationPlan) {
|
|
106
105
|
logInfo(`[CLAIM] Running ${verification.source}: ${verification.label}`, { cwd, section: "script", progressOnly: true });
|
|
107
106
|
}
|
|
108
|
-
const
|
|
109
|
-
|
|
107
|
+
const invocation = await runVerificationPlan({ cwd, candidate: verificationCandidate, plan: verificationPlan, signal: input.signal, root: repositoryRoot ?? cwd });
|
|
108
|
+
// Petition owns the run: abort cancels and awaitTerminal observes cleanup.
|
|
109
|
+
const evidence = await invocation.awaitTerminal();
|
|
110
|
+
const runnerFailure = evidence.results.find((result) => result.execution.status === "runner_failed");
|
|
110
111
|
if (runnerFailure?.execution.status === "runner_failed") {
|
|
111
112
|
return {
|
|
112
113
|
pass: false,
|
|
@@ -120,7 +121,7 @@ export function createCommandVerificationGate() {
|
|
|
120
121
|
},
|
|
121
122
|
};
|
|
122
123
|
}
|
|
123
|
-
const commandFailure = evidence.find((result) => result.execution.status === "executed" && result.execution.code !== 0);
|
|
124
|
+
const commandFailure = evidence.results.find((result) => result.execution.status === "executed" && result.execution.code !== 0);
|
|
124
125
|
if (commandFailure?.execution.status === "executed") {
|
|
125
126
|
return {
|
|
126
127
|
pass: false,
|
|
@@ -134,7 +135,7 @@ export function createCommandVerificationGate() {
|
|
|
134
135
|
},
|
|
135
136
|
};
|
|
136
137
|
}
|
|
137
|
-
return { pass: true, verification: [...evidence] };
|
|
138
|
+
return { pass: true, verification: [...evidence.results] };
|
|
138
139
|
},
|
|
139
140
|
};
|
|
140
141
|
}
|
|
@@ -193,7 +194,7 @@ export function createAgentReviewGate() {
|
|
|
193
194
|
authority: { kind: "repository" },
|
|
194
195
|
binding: { kind: "commission", commissionId: contractId },
|
|
195
196
|
});
|
|
196
|
-
const reviewHeadBeforeWrite = (await
|
|
197
|
+
const reviewHeadBeforeWrite = (await requireCommitCoordinate(cwd, "HEAD")).commit;
|
|
197
198
|
const raw = subagentResult.finalMessage.trim();
|
|
198
199
|
const passed = raw.startsWith(REVIEW_PASS_TOKEN);
|
|
199
200
|
const observations = passed ? raw.slice(REVIEW_PASS_TOKEN.length).trim() || undefined : undefined;
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { FlowError } from "../../flow-error.js";
|
|
2
|
-
import {
|
|
2
|
+
import { requireCommitCoordinate } from "../../git/commits.js";
|
|
3
3
|
const CONCURRENT_MODIFICATION_CODE = "CONCURRENT_MODIFICATION";
|
|
4
4
|
export async function assertHeadUnchanged(cwd, expectedHead, operation) {
|
|
5
|
-
const git = createGit(cwd);
|
|
6
5
|
const [expectedCommit, currentHead] = await Promise.all([
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
requireCommitCoordinate(cwd, expectedHead),
|
|
7
|
+
requireCommitCoordinate(cwd, "HEAD"),
|
|
9
8
|
]);
|
|
10
|
-
if (currentHead.
|
|
9
|
+
if (currentHead.commit === expectedCommit.commit)
|
|
11
10
|
return;
|
|
12
|
-
throw new FlowError(CONCURRENT_MODIFICATION_CODE, `concurrent ${operation} modification detected: repository HEAD changed from ${expectedHead} to ${currentHead.
|
|
11
|
+
throw new FlowError(CONCURRENT_MODIFICATION_CODE, `concurrent ${operation} modification detected: repository HEAD changed from ${expectedHead} to ${currentHead.commit} while the ${operation} was running`);
|
|
13
12
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { createOathGate } from "./petition-claim-gates.js";
|
|
2
|
-
import { assertFoldedPetitionable, } from "./petition.js";
|
|
2
|
+
import { assertFoldedPetitionable, precedingRenewSealResult, } from "./petition.js";
|
|
3
3
|
import { captureFoldedSeal } from "../seal.js";
|
|
4
4
|
import { withContractCarrierSession } from "../contract-carrier.js";
|
|
5
5
|
import { FlowError } from "../../flow-error.js";
|
|
6
|
+
import { normalizeScopePattern } from "../scope.js";
|
|
6
7
|
export const PETITION_PREVIEW_GATE_NAMES = [
|
|
7
8
|
"contract",
|
|
8
9
|
"freshness",
|
|
@@ -44,6 +45,7 @@ export async function previewPetition(input) {
|
|
|
44
45
|
const base = session.lifecycle.pins.base;
|
|
45
46
|
const target = { ref: session.lifecycle.pins.target.ref, branch: session.lifecycle.pins.target.ref.slice("refs/heads/".length) };
|
|
46
47
|
const context = { lifecycle: session.lifecycle, deliveryCwd: session.target.root, base, target };
|
|
48
|
+
const appendScope = (input.appendScope ?? []).map(normalizeScopePattern);
|
|
47
49
|
const gates = [{ name: "contract", result: "pass" }];
|
|
48
50
|
const freshness = await gate("freshness", async () => {
|
|
49
51
|
if (!session.pins.target.head || context.base !== session.pins.target.head) {
|
|
@@ -58,15 +60,28 @@ export async function previewPetition(input) {
|
|
|
58
60
|
})
|
|
59
61
|
: notEvaluated("arc");
|
|
60
62
|
gates.push(arc);
|
|
61
|
-
const state = await gate("state", () => {
|
|
62
|
-
assertFoldedPetitionable(input.contractId, context.lifecycle);
|
|
63
|
+
const state = await gate("state", async () => {
|
|
64
|
+
await assertFoldedPetitionable(input.contractId, context.lifecycle, session.repository, session.stableRoot);
|
|
63
65
|
});
|
|
64
66
|
gates.push(state);
|
|
65
67
|
let seal;
|
|
68
|
+
let scopeWarning = null;
|
|
66
69
|
if (freshness.result === "pass" && arc.result === "pass" && state.result === "pass") {
|
|
67
70
|
try {
|
|
68
|
-
const
|
|
69
|
-
|
|
71
|
+
const precedingRenewSeal = appendScope.length === 0
|
|
72
|
+
? await precedingRenewSealResult(context.deliveryCwd, context.lifecycle)
|
|
73
|
+
: null;
|
|
74
|
+
const capture = precedingRenewSeal
|
|
75
|
+
? null
|
|
76
|
+
: await captureFoldedSeal(context.deliveryCwd, context.lifecycle, "HEAD", appendScope, "warn");
|
|
77
|
+
const scope = precedingRenewSeal ?? capture;
|
|
78
|
+
if (scope?.scopeAudit.violations.length) {
|
|
79
|
+
scopeWarning = {
|
|
80
|
+
violations: [...scope.scopeAudit.violations],
|
|
81
|
+
evidence: [...scope.scopeViolationEvidence],
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
seal = { name: "seal", result: "pass", detail: sealFileDetail(scope?.diffstat.files ?? 0) };
|
|
70
85
|
}
|
|
71
86
|
catch (error) {
|
|
72
87
|
seal = { name: "seal", result: "failed", detail: failureDetail(error) };
|
|
@@ -102,6 +117,7 @@ export async function previewPetition(input) {
|
|
|
102
117
|
contract: input.contractId,
|
|
103
118
|
target: context.target.ref,
|
|
104
119
|
arc: { source: { arc: context.lifecycle.phase === "active" ? context.lifecycle.intent.arc : null, fence: context.lifecycle.pins.delivery } },
|
|
120
|
+
scopeWarning,
|
|
105
121
|
gates,
|
|
106
122
|
verdict: gates.some((item) => item.result === "failed") ? "would_fail" : "would_succeed",
|
|
107
123
|
};
|
|
@@ -3,14 +3,44 @@ import { parseCommandMarkdown } from "../command-markdown.js";
|
|
|
3
3
|
import { runLifecycle } from "../lifecycle-runner.js";
|
|
4
4
|
import { queueMembership, readQueueReadModel } from "./queue.js";
|
|
5
5
|
import { captureFoldedSeal } from "../seal.js";
|
|
6
|
-
import { currentLifecycleState } from "../repository-ledger/read-model.js";
|
|
6
|
+
import { currentLifecycleState, lookupCurrentContract } from "../repository-ledger/read-model.js";
|
|
7
7
|
import { assertCleanTrackedWorkingTree } from "../../git/worktree.js";
|
|
8
|
-
import { normalizeScopePattern } from "../scope.js";
|
|
9
|
-
|
|
8
|
+
import { normalizeScopePattern, scopePathEvidence } from "../scope.js";
|
|
9
|
+
import { readRemainingTaskNeedsForSettlement } from "../task/settlement-policy.js";
|
|
10
|
+
export async function assertFoldedPetitionable(contractId, model, repository, stableRoot) {
|
|
10
11
|
const state = currentLifecycleState(model);
|
|
11
12
|
if (state.state !== "active" && state.state !== "bound") {
|
|
12
13
|
throw new FlowError("INTERNAL_STATE", `contract ${contractId} cannot petition from state ${state.state}`);
|
|
13
14
|
}
|
|
15
|
+
const unavailable = model.causal.after.filter((dependency) => {
|
|
16
|
+
const prerequisite = lookupCurrentContract(repository, dependency);
|
|
17
|
+
return prerequisite.status !== "healthy" ||
|
|
18
|
+
(prerequisite.model.phase !== "claimed" && prerequisite.model.phase !== "forfeited");
|
|
19
|
+
});
|
|
20
|
+
if (unavailable.length > 0) {
|
|
21
|
+
throw new FlowError("INTERNAL_STATE", `contract ${contractId} cannot petition before after dependencies settle: ${unavailable.join(", ")}`);
|
|
22
|
+
}
|
|
23
|
+
const pendingNeeds = (await readRemainingTaskNeedsForSettlement(stableRoot, model.causal.needs))
|
|
24
|
+
.filter((need) => !need.satisfied);
|
|
25
|
+
if (pendingNeeds.length > 0) {
|
|
26
|
+
throw new FlowError("INTERNAL_STATE", `contract ${contractId} cannot petition before task needs settle: ${pendingNeeds.map((need) => need.id).join(", ")}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/** Rehydrate the seal consumed by an immediately preceding renew without auditing a new segment. */
|
|
30
|
+
export async function precedingRenewSealResult(cwd, lifecycle, renewIndex = lifecycle.history.length - 1) {
|
|
31
|
+
const renew = lifecycle.history[renewIndex]?.entry;
|
|
32
|
+
const seal = lifecycle.history[renewIndex - 1];
|
|
33
|
+
if (renew?.kind !== "renew" || seal?.entry.kind !== "seal" || seal.entry.data.sealedBy !== "renew")
|
|
34
|
+
return null;
|
|
35
|
+
return {
|
|
36
|
+
...seal.entry.data,
|
|
37
|
+
receipts: [...seal.entry.data.receipts],
|
|
38
|
+
diffstat: { ...seal.entry.data.diffstat },
|
|
39
|
+
scopeAudit: { violations: [...seal.entry.data.scopeAudit.violations] },
|
|
40
|
+
scopeViolationEvidence: await scopePathEvidence(cwd, lifecycle.effectiveScope, seal.entry.data.scopeAudit.violations),
|
|
41
|
+
sealedBy: "renew",
|
|
42
|
+
ledgerHead: seal.objectId,
|
|
43
|
+
};
|
|
14
44
|
}
|
|
15
45
|
function nextSettlementSeat(read, target) {
|
|
16
46
|
let floor = 0;
|
|
@@ -68,7 +98,7 @@ export async function petitionContract(input) {
|
|
|
68
98
|
const branch = target.slice("refs/heads/".length);
|
|
69
99
|
throw new FlowError("INTERNAL_STATE", `contract ${input.contractId} must renew onto ${branch} before petition`, { facts: { kind: "needs_renew", contractId: input.contractId } });
|
|
70
100
|
}
|
|
71
|
-
assertFoldedPetitionable(input.contractId, session.lifecycle);
|
|
101
|
+
await assertFoldedPetitionable(input.contractId, session.lifecycle, session.repository, session.stableRoot);
|
|
72
102
|
return { lifecycle: session.lifecycle, deliveryCwd };
|
|
73
103
|
},
|
|
74
104
|
prepare: async (admission) => {
|
|
@@ -88,7 +118,7 @@ export async function petitionContract(input) {
|
|
|
88
118
|
receipts: [...acceptedSeal.data.receipts],
|
|
89
119
|
diffstat: { ...acceptedSeal.data.diffstat },
|
|
90
120
|
scopeAudit: { violations: [...acceptedSeal.data.scopeAudit.violations] },
|
|
91
|
-
scopeViolationEvidence:
|
|
121
|
+
scopeViolationEvidence: await scopePathEvidence(input.cwd, admission.business.lifecycle.effectiveScope, acceptedSeal.data.scopeAudit.violations),
|
|
92
122
|
};
|
|
93
123
|
prefixIndex -= 1;
|
|
94
124
|
}
|
|
@@ -101,13 +131,14 @@ export async function petitionContract(input) {
|
|
|
101
131
|
return {
|
|
102
132
|
additions: [],
|
|
103
133
|
capture,
|
|
134
|
+
precedingRenewSeal: await precedingRenewSealResult(input.cwd, admission.business.lifecycle, admission.business.lifecycle.history.findIndex(({ objectId }) => objectId === transaction.entryObjectIds.at(-1)) - 1),
|
|
104
135
|
seat: accepted.data.seat,
|
|
105
136
|
targetHead: accepted.data.intent === "claim" ? accepted.data.targetHead : admission.pins.target.head ?? "",
|
|
106
137
|
existing: { transactionObjectId: transaction.transactionObjectId },
|
|
107
138
|
};
|
|
108
139
|
}
|
|
109
140
|
const scopeEntry = patterns.length === 0 ? null : {
|
|
110
|
-
v:
|
|
141
|
+
v: 2,
|
|
111
142
|
kind: "amend",
|
|
112
143
|
contract: input.contractId,
|
|
113
144
|
at,
|
|
@@ -118,12 +149,12 @@ export async function petitionContract(input) {
|
|
|
118
149
|
if (!deliveryCwd)
|
|
119
150
|
throw new FlowError("INTERNAL_STATE", `petition ${input.contractId} is missing its delivery worktree`);
|
|
120
151
|
const lifecycle = admission.business.lifecycle;
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
const capture =
|
|
152
|
+
const precedingRenewSeal = patterns.length === 0
|
|
153
|
+
? await precedingRenewSealResult(input.cwd, lifecycle)
|
|
154
|
+
: null;
|
|
155
|
+
const capture = precedingRenewSeal
|
|
125
156
|
? null
|
|
126
|
-
: await captureFoldedSeal(deliveryCwd, lifecycle, "HEAD", patterns);
|
|
157
|
+
: await captureFoldedSeal(deliveryCwd, lifecycle, "HEAD", patterns, "warn");
|
|
127
158
|
let sealEntry = null;
|
|
128
159
|
if (capture) {
|
|
129
160
|
const { scopeViolationEvidence: _evidence, ...durableCapture } = capture;
|
|
@@ -145,6 +176,7 @@ export async function petitionContract(input) {
|
|
|
145
176
|
return {
|
|
146
177
|
additions,
|
|
147
178
|
capture,
|
|
179
|
+
precedingRenewSeal,
|
|
148
180
|
seat: nextSettlementSeat(admission.repository, admission.pins.target.ref),
|
|
149
181
|
targetHead,
|
|
150
182
|
};
|
|
@@ -169,7 +201,7 @@ export async function petitionContract(input) {
|
|
|
169
201
|
const base = session.lifecycle.pins.base;
|
|
170
202
|
if (base !== session.pins.target.head)
|
|
171
203
|
return { code: "PETITION_TARGET_CHANGED", message: "petition target/base facts changed during preparation" };
|
|
172
|
-
assertFoldedPetitionable(input.contractId, session.lifecycle);
|
|
204
|
+
await assertFoldedPetitionable(input.contractId, session.lifecycle, session.repository, session.stableRoot);
|
|
173
205
|
return undefined;
|
|
174
206
|
},
|
|
175
207
|
effect: async ({ context, prepared }) => {
|
|
@@ -208,15 +240,15 @@ export async function petitionContract(input) {
|
|
|
208
240
|
? await context.replayCommitted({ transactionObjectId: prepared.existing.transactionObjectId })
|
|
209
241
|
: await context.publishAuthoritative({
|
|
210
242
|
verb: "petition",
|
|
211
|
-
admittedLedgerHead: context.session.
|
|
212
|
-
acceptedLifecycleTail: context.session.lifecycle.
|
|
243
|
+
admittedLedgerHead: context.session.pins.repositoryLedgerHead,
|
|
244
|
+
acceptedLifecycleTail: context.session.lifecycle.contractHead,
|
|
213
245
|
entries: additions,
|
|
214
246
|
});
|
|
215
247
|
if (result.status === "indeterminate") {
|
|
216
|
-
return { kind: "precommit-effects-only", authoritativeHead: result.currentHead ?? context.session.
|
|
248
|
+
return { kind: "precommit-effects-only", authoritativeHead: result.currentHead ?? context.session.pins.repositoryLedgerHead };
|
|
217
249
|
}
|
|
218
250
|
if (result.status !== "committed") {
|
|
219
|
-
const authoritativeHead = result.currentHead ?? context.session.
|
|
251
|
+
const authoritativeHead = result.currentHead ?? context.session.pins.repositoryLedgerHead;
|
|
220
252
|
return { kind: "refused-before-effects", authoritativeHead, refusal: { code: "LEDGER_CAS_CONFLICT", message: `contract ${input.contractId} changed during petition` } };
|
|
221
253
|
}
|
|
222
254
|
if (input.reviewPersistence) {
|
|
@@ -254,7 +286,9 @@ export async function petitionContract(input) {
|
|
|
254
286
|
position,
|
|
255
287
|
seat: prepared.seat,
|
|
256
288
|
reviewHistoryPath,
|
|
257
|
-
sealed: prepared.capture
|
|
289
|
+
sealed: prepared.capture
|
|
290
|
+
? { ...prepared.capture, sealedBy: "petition", ledgerHead: result.transaction.entryObjectIds[result.transaction.entries.findIndex((entry) => entry.kind === "seal")] }
|
|
291
|
+
: prepared.precedingRenewSeal ?? undefined,
|
|
258
292
|
},
|
|
259
293
|
};
|
|
260
294
|
},
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { FlowError } from "../../flow-error.js";
|
|
2
2
|
import { readCurrentContractContext } from "../context.js";
|
|
3
3
|
import { convergeContractCarrier, withContractCarrierSession } from "../contract-carrier.js";
|
|
4
|
-
import {
|
|
4
|
+
import { requireCommitCoordinate } from "../../git/commits.js";
|
|
5
5
|
import { getCurrentBranch, requireGitRepository } from "../../git/branches.js";
|
|
6
|
-
import { getLatestCommitHash } from "../../git/staging.js";
|
|
7
6
|
import { readContractLog } from "../log.js";
|
|
8
7
|
import { scopePathEvidence } from "../scope.js";
|
|
9
8
|
import { nextReviewResponseNumber } from "../transcripts.js";
|
|
@@ -45,15 +44,17 @@ async function pinnedCloseCandidate(cwd, contractId, signal) {
|
|
|
45
44
|
if (session.target.status !== "available" || !session.pins.delivery.head) {
|
|
46
45
|
throw new FlowError("INTERNAL_STATE", session.target.status === "available" ? `contract ${contractId} delivery ref does not resolve` : session.target.reason);
|
|
47
46
|
}
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
git.raw(["rev-parse", "--verify", "HEAD^{commit}"]),
|
|
51
|
-
git.raw(["rev-parse", "--verify", "HEAD^{tree}"]),
|
|
52
|
-
]);
|
|
53
|
-
if (head.trim() !== session.pins.delivery.head) {
|
|
47
|
+
const coordinate = await requireCommitCoordinate(session.target.root, "HEAD");
|
|
48
|
+
if (coordinate.commit !== session.pins.delivery.head) {
|
|
54
49
|
throw new FlowError("INTERNAL_STATE", `contract ${contractId} delivery head changed while petition was being prepared`);
|
|
55
50
|
}
|
|
56
|
-
|
|
51
|
+
const bind = session.lifecycle.history.find(({ entry }) => entry.kind === "bind")?.entry;
|
|
52
|
+
return {
|
|
53
|
+
deliveryCwd: session.target.root,
|
|
54
|
+
candidate: { head: coordinate.commit, tree: coordinate.tree },
|
|
55
|
+
contractVerification: contractVerificationDeclarations(session.lifecycle),
|
|
56
|
+
preparation: bind?.kind === "bind" && bind.data.workspace === "worktree" ? bind.data.bootstrap ?? null : null,
|
|
57
|
+
};
|
|
57
58
|
});
|
|
58
59
|
}
|
|
59
60
|
async function loadCloseContext(cwd, requestedContractId, signal) {
|
|
@@ -66,6 +67,7 @@ async function loadCloseContext(cwd, requestedContractId, signal) {
|
|
|
66
67
|
deliveryCwd: pinned.deliveryCwd,
|
|
67
68
|
verificationCandidate: pinned.candidate,
|
|
68
69
|
contractVerification: pinned.contractVerification,
|
|
70
|
+
verificationPreparation: pinned.preparation,
|
|
69
71
|
keiyakuBranch: `keiyaku/${requestedContractId}`,
|
|
70
72
|
baseBranch: lifecycle.pins.target.ref.slice("refs/heads/".length),
|
|
71
73
|
contractId: requestedContractId,
|
|
@@ -88,6 +90,7 @@ async function loadCloseContext(cwd, requestedContractId, signal) {
|
|
|
88
90
|
deliveryCwd: pinned.deliveryCwd,
|
|
89
91
|
verificationCandidate: pinned.candidate,
|
|
90
92
|
contractVerification: pinned.contractVerification,
|
|
93
|
+
verificationPreparation: pinned.preparation,
|
|
91
94
|
keiyakuBranch,
|
|
92
95
|
baseBranch: contractContext.baseBranch,
|
|
93
96
|
contractId: contractContext.contractId,
|
|
@@ -107,14 +110,15 @@ async function throwClaimGateFailure(failure, _input, context, _dependencies) {
|
|
|
107
110
|
}
|
|
108
111
|
async function runClaimGatePipelineOrThrow(input, context, dependencies) {
|
|
109
112
|
const deliveryCwd = context.deliveryCwd ?? context.cwd;
|
|
110
|
-
const verificationCandidate = context.verificationCandidate ?? {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
};
|
|
113
|
+
const verificationCandidate = context.verificationCandidate ?? await (async () => {
|
|
114
|
+
const coordinate = await requireCommitCoordinate(deliveryCwd, "HEAD");
|
|
115
|
+
return { head: coordinate.commit, tree: coordinate.tree };
|
|
116
|
+
})();
|
|
117
|
+
const verificationPreparation = context.verificationPreparation ?? null;
|
|
114
118
|
const gateConfig = await dependencies.resolveClaimGateConfig(context.cwd);
|
|
115
119
|
const repositoryGates = gateConfig.repositoryGates ?? gateConfig.verificationCommands ?? [];
|
|
116
120
|
const verificationPlan = composeVerificationPlan({ contract: context.contractVerification ?? [], repositoryGates, timeoutMs: gateConfig.verificationTimeoutMs });
|
|
117
|
-
const
|
|
121
|
+
const repositoryRoot = await stableRepoRoot(context.cwd);
|
|
118
122
|
let review;
|
|
119
123
|
let reviewHeadBeforeWrite;
|
|
120
124
|
let reviewerActor;
|
|
@@ -123,11 +127,12 @@ async function runClaimGatePipelineOrThrow(input, context, dependencies) {
|
|
|
123
127
|
for (const gate of buildClaimGatePipeline()) {
|
|
124
128
|
const result = await gate.evaluate({
|
|
125
129
|
cwd: deliveryCwd,
|
|
126
|
-
|
|
130
|
+
repositoryRoot,
|
|
127
131
|
contractId: context.contractId,
|
|
128
132
|
input,
|
|
129
133
|
verificationPlan,
|
|
130
134
|
verificationCandidate,
|
|
135
|
+
verificationPreparation,
|
|
131
136
|
baseBranch: dependencies.baseBranch,
|
|
132
137
|
reviewNumber: dependencies.reviewNumber,
|
|
133
138
|
});
|
|
@@ -103,12 +103,12 @@ export async function verdictContract(input) {
|
|
|
103
103
|
value: { result: admission.business.existing.result, summary: admission.business.existing.summary },
|
|
104
104
|
};
|
|
105
105
|
}
|
|
106
|
-
const committed = await context.publishAuthoritative({ verb: "verdict", admittedLedgerHead: admission.ledgerHead, acceptedLifecycleTail: context.session.lifecycle.
|
|
106
|
+
const committed = await context.publishAuthoritative({ verb: "verdict", admittedLedgerHead: admission.ledgerHead, acceptedLifecycleTail: context.session.lifecycle.contractHead, entries: [prepared.entry] });
|
|
107
107
|
if (committed.status === "indeterminate") {
|
|
108
108
|
return { kind: "precommit-effects-only", authoritativeHead: committed.currentHead ?? admission.ledgerHead };
|
|
109
109
|
}
|
|
110
110
|
if (committed.status !== "committed") {
|
|
111
|
-
return { kind: "refused-before-effects", authoritativeHead: committed.currentHead ?? context.session.
|
|
111
|
+
return { kind: "refused-before-effects", authoritativeHead: committed.currentHead ?? context.session.pins.repositoryLedgerHead, refusal: { code: "LEDGER_CAS_CONFLICT", message: `contract ${input.contractId} changed during verdict` } };
|
|
112
112
|
}
|
|
113
113
|
return {
|
|
114
114
|
kind: "committed",
|