@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
|
@@ -4,7 +4,8 @@ import * as path from "node:path";
|
|
|
4
4
|
import { FlowError, asMessage } from "../../flow-error.js";
|
|
5
5
|
import { createGit, wrapGitError } from "../../git/core.js";
|
|
6
6
|
import { readStructuredDiff } from "../../git/diff/structured.js";
|
|
7
|
-
import {
|
|
7
|
+
import { abortDetachedRenewChain, buildDetachedRenewChain, renewScratchName } from "../renew-rewrite.js";
|
|
8
|
+
import { isBootstrapSuccess, runWorktreeBootstrap } from "../worktree-bootstrap.js";
|
|
8
9
|
async function resolveObject(cwd, revision, kind) {
|
|
9
10
|
try {
|
|
10
11
|
return (await createGit(cwd).raw(["rev-parse", "--verify", `${revision}^{${kind}}`])).trim();
|
|
@@ -24,14 +25,25 @@ export async function createAuditCandidate(input) {
|
|
|
24
25
|
await candidateGit.raw(["checkout", "--quiet", "-B", `keiyaku/${input.contractId}`, input.frozen.coordinates.delivery.head]);
|
|
25
26
|
if (input.frozen.base === input.frozen.coordinates.target.head)
|
|
26
27
|
return { cwd, rehearsal: { status: "fresh" }, candidate: { base: input.frozen.coordinates.target.head, head: input.frozen.coordinates.delivery.head, tree: input.frozen.coordinates.delivery.tree, rewritten: false } };
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
const scratchName = renewScratchName(`audit-${input.contractId}-${input.frozen.coordinates.delivery.head}`);
|
|
29
|
+
const deliveryCommits = (await candidateGit.raw(["rev-list", "--reverse", "--ancestry-path", `${input.frozen.base}..${input.frozen.coordinates.delivery.head}`])).split(/\r?\n/).filter(Boolean);
|
|
30
|
+
const rewrite = await buildDetachedRenewChain({
|
|
31
|
+
cwd,
|
|
32
|
+
scratchName,
|
|
33
|
+
observedLedgerHead: input.frozen.coordinates.ledger.head,
|
|
34
|
+
deliveryRef: input.frozen.coordinates.delivery.ref,
|
|
35
|
+
targetRef: input.frozen.coordinates.target.ref,
|
|
36
|
+
effectiveScope: input.frozen.scope,
|
|
37
|
+
oldBase: input.frozen.base,
|
|
38
|
+
oldHead: input.frozen.coordinates.delivery.head,
|
|
39
|
+
newBase: input.frozen.coordinates.target.head,
|
|
40
|
+
deliveryCommits,
|
|
41
|
+
});
|
|
42
|
+
if (rewrite.status === "conflict") {
|
|
43
|
+
await abortDetachedRenewChain({ cwd, scratchName });
|
|
44
|
+
return { cwd, rehearsal: { status: "conflict", paths: [...rewrite.conflictPaths], pathCapture: "captured" }, candidate: null };
|
|
34
45
|
}
|
|
46
|
+
await candidateGit.raw(["reset", "--hard", rewrite.newHead]);
|
|
35
47
|
const [head, tree] = await Promise.all([resolveObject(cwd, "HEAD", "commit"), resolveObject(cwd, "HEAD", "tree")]);
|
|
36
48
|
return { cwd, rehearsal: { status: "clean", oldBase: input.frozen.base, newBase: input.frozen.coordinates.target.head, oldHead: input.frozen.coordinates.delivery.head }, candidate: { base: input.frozen.coordinates.target.head, head, tree, rewritten: true } };
|
|
37
49
|
}
|
|
@@ -44,5 +56,94 @@ export async function readAuditCandidateDiff(candidateResult) {
|
|
|
44
56
|
const candidate = candidateResult.candidate;
|
|
45
57
|
if (!candidate)
|
|
46
58
|
return null;
|
|
47
|
-
return readStructuredDiff(candidateResult.cwd, candidate.base, candidate.head
|
|
59
|
+
return readStructuredDiff(candidateResult.cwd, candidate.base, candidate.head);
|
|
60
|
+
}
|
|
61
|
+
async function readCandidateIntegrity(cwd) {
|
|
62
|
+
const git = createGit(cwd);
|
|
63
|
+
const [head, tree, status] = await Promise.all([
|
|
64
|
+
resolveObject(cwd, "HEAD", "commit"),
|
|
65
|
+
resolveObject(cwd, "HEAD", "tree"),
|
|
66
|
+
git.raw(["status", "--porcelain=v1", "-z", "--untracked-files=no"]),
|
|
67
|
+
]);
|
|
68
|
+
return { head, tree, trackedChanges: status.split("\0").filter(Boolean) };
|
|
69
|
+
}
|
|
70
|
+
function preparationPlan(plan) {
|
|
71
|
+
return { command: [...plan.command], timeoutMs: plan.timeoutMs };
|
|
72
|
+
}
|
|
73
|
+
function preparationOutput(observation) {
|
|
74
|
+
return {
|
|
75
|
+
...(observation.stdoutTail !== undefined ? { stdoutTail: observation.stdoutTail } : {}),
|
|
76
|
+
...(observation.stdoutTruncated ? { stdoutTruncated: true } : {}),
|
|
77
|
+
...(observation.stderrTail !== undefined ? { stderrTail: observation.stderrTail } : {}),
|
|
78
|
+
...(observation.stderrTruncated ? { stderrTruncated: true } : {}),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function bootstrapResultDescription(result) {
|
|
82
|
+
if (result.kind === "exit")
|
|
83
|
+
return `exit ${result.code}`;
|
|
84
|
+
if (result.kind === "signal")
|
|
85
|
+
return `signal ${result.signal}`;
|
|
86
|
+
if (result.kind === "timeout")
|
|
87
|
+
return "timeout";
|
|
88
|
+
return `spawn: ${result.message}`;
|
|
89
|
+
}
|
|
90
|
+
function observationFailure(plan, candidate, observation) {
|
|
91
|
+
return {
|
|
92
|
+
status: "failed",
|
|
93
|
+
plan: preparationPlan(plan),
|
|
94
|
+
candidate: { head: candidate.head, tree: candidate.tree },
|
|
95
|
+
result: observation.result,
|
|
96
|
+
durationMs: observation.durationMs,
|
|
97
|
+
...preparationOutput(observation),
|
|
98
|
+
cause: { code: "CANDIDATE_BOOTSTRAP_FAILED", message: `candidate bootstrap failed with ${bootstrapResultDescription(observation.result)}` },
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
export async function prepareAuditCandidate(input, dependencies = {}) {
|
|
102
|
+
const candidate = input.candidateResult.candidate;
|
|
103
|
+
if (!candidate)
|
|
104
|
+
return { status: "not-required", reason: "candidate_unavailable" };
|
|
105
|
+
if (!input.verificationRequired)
|
|
106
|
+
return { status: "not-required", reason: "verification_not_configured" };
|
|
107
|
+
if (!input.plan)
|
|
108
|
+
return { status: "not-configured", candidate: { head: candidate.head, tree: candidate.tree } };
|
|
109
|
+
const runBootstrap = dependencies.runBootstrap ?? runWorktreeBootstrap;
|
|
110
|
+
const observation = await runBootstrap({ cwd: input.candidateResult.cwd, plan: input.plan, captureOutput: true });
|
|
111
|
+
if (!isBootstrapSuccess(observation))
|
|
112
|
+
return observationFailure(input.plan, candidate, observation);
|
|
113
|
+
let integrity;
|
|
114
|
+
try {
|
|
115
|
+
integrity = await (dependencies.readIntegrity ?? readCandidateIntegrity)(input.candidateResult.cwd);
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
return {
|
|
119
|
+
status: "failed",
|
|
120
|
+
plan: preparationPlan(input.plan),
|
|
121
|
+
candidate: { head: candidate.head, tree: candidate.tree },
|
|
122
|
+
result: observation.result,
|
|
123
|
+
durationMs: observation.durationMs,
|
|
124
|
+
...preparationOutput(observation),
|
|
125
|
+
cause: { code: "CANDIDATE_INTEGRITY_UNKNOWN", message: asMessage(error) },
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
if (integrity.head !== candidate.head || integrity.tree !== candidate.tree || integrity.trackedChanges.length > 0) {
|
|
129
|
+
return {
|
|
130
|
+
status: "failed",
|
|
131
|
+
plan: preparationPlan(input.plan),
|
|
132
|
+
candidate: { head: candidate.head, tree: candidate.tree },
|
|
133
|
+
result: observation.result,
|
|
134
|
+
durationMs: observation.durationMs,
|
|
135
|
+
...preparationOutput(observation),
|
|
136
|
+
cause: { code: "CANDIDATE_INTEGRITY_CHANGED", message: "candidate HEAD, tree, or tracked worktree changed during preparation" },
|
|
137
|
+
integrity,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
status: "succeeded",
|
|
142
|
+
plan: preparationPlan(input.plan),
|
|
143
|
+
candidate: { head: candidate.head, tree: candidate.tree },
|
|
144
|
+
result: { kind: "exit", code: 0 },
|
|
145
|
+
durationMs: observation.durationMs,
|
|
146
|
+
...preparationOutput(observation),
|
|
147
|
+
integrity,
|
|
148
|
+
};
|
|
48
149
|
}
|
|
@@ -3,17 +3,12 @@ import * as path from "node:path";
|
|
|
3
3
|
import { createHash } from "node:crypto";
|
|
4
4
|
import { FlowError, asMessage } from "../../flow-error.js";
|
|
5
5
|
import { createGit, wrapGitError } from "../../git/core.js";
|
|
6
|
-
import {
|
|
6
|
+
import { effectiveCriteria } from "../amendment-criteria.js";
|
|
7
7
|
import { hasStrandableProjectionGeneration } from "../projection/index.js";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import { normalizeSectionTitle } from "../markdown/titles.js";
|
|
13
|
-
import { commissionTarget, latestBase } from "../status/drift.js";
|
|
14
|
-
import { deriveContractState, isTerminalState } from "../status/lifecycle.js";
|
|
15
|
-
import { resolveContractDeliveryCwd } from "../worktree-path.js";
|
|
16
|
-
import { KEIYAKU_FILE } from "../../keiyaku.js";
|
|
8
|
+
import { admitRepositoryLifecycleContract, } from "../repository-ledger/admission.js";
|
|
9
|
+
import { lookupCurrentContract, readRepositoryLifecycle, } from "../repository-ledger/read-model.js";
|
|
10
|
+
import { deriveQueueReadModel, queueMembership } from "../settlement/queue.js";
|
|
11
|
+
import { findRegisteredContractWorktreePathFromModel } from "../worktree-path.js";
|
|
17
12
|
async function resolveObject(cwd, revision, kind) {
|
|
18
13
|
const git = createGit(cwd);
|
|
19
14
|
try {
|
|
@@ -23,45 +18,62 @@ async function resolveObject(cwd, revision, kind) {
|
|
|
23
18
|
throw wrapGitError(`rev-parse ${revision}^{${kind}}`, error, cwd);
|
|
24
19
|
}
|
|
25
20
|
}
|
|
26
|
-
export function appendedScopeTerms(
|
|
27
|
-
return
|
|
21
|
+
export function appendedScopeTerms(model) {
|
|
22
|
+
return model.history.flatMap(({ entry }) => entry.kind === "amend" && entry.data.scopeDelta ? entry.data.scopeDelta.add : []);
|
|
28
23
|
}
|
|
29
|
-
|
|
30
|
-
const bind =
|
|
31
|
-
if (!bind)
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
24
|
+
function auditCriteria(model) {
|
|
25
|
+
const bind = model.history.find(({ entry }) => entry.kind === "bind")?.entry;
|
|
26
|
+
if (!bind || bind.kind !== "bind")
|
|
27
|
+
throw new FlowError("INTERNAL_STATE", `contract ${model.contract} is missing folded bind terms`);
|
|
28
|
+
const amendments = model.history.flatMap(({ entry }) => entry.kind === "amend" ? [entry.data.amendment] : []);
|
|
29
|
+
return effectiveCriteria(bind.data.criteria, amendments);
|
|
30
|
+
}
|
|
31
|
+
function repositoryLifecycleDiseaseError(outcome) {
|
|
32
|
+
return new FlowError("INTERNAL_STATE", `contract ${outcome.contract} is unavailable because repository lifecycle evidence is ${outcome.status}`, {
|
|
33
|
+
facts: {
|
|
34
|
+
kind: "repository_lifecycle_disease",
|
|
35
|
+
contractId: outcome.contract,
|
|
36
|
+
status: outcome.status,
|
|
37
|
+
diagnostics: outcome.diagnostics.map((diagnostic) => ({
|
|
38
|
+
objectId: diagnostic.objectId,
|
|
39
|
+
code: diagnostic.code,
|
|
40
|
+
detail: diagnostic.detail,
|
|
41
|
+
...(diagnostic.contract === undefined ? {} : { contract: diagnostic.contract }),
|
|
42
|
+
})),
|
|
43
|
+
},
|
|
44
|
+
});
|
|
48
45
|
}
|
|
49
46
|
async function pinLedgerCoordinates(input) {
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
47
|
+
const repository = await readRepositoryLifecycle(input.root);
|
|
48
|
+
const admission = admitRepositoryLifecycleContract(repository, input.contractId);
|
|
49
|
+
let model;
|
|
50
|
+
switch (admission.status) {
|
|
51
|
+
case "healthy":
|
|
52
|
+
model = admission.model;
|
|
53
|
+
break;
|
|
54
|
+
case "diseased":
|
|
55
|
+
case "isolated":
|
|
56
|
+
case "indeterminate":
|
|
57
|
+
throw repositoryLifecycleDiseaseError(admission);
|
|
58
|
+
case "absent":
|
|
59
|
+
throw new FlowError("CONTRACT_NOT_FOUND", `contract ${admission.contract} not found`);
|
|
60
|
+
}
|
|
61
|
+
if (repository.observedLedgerHead === null)
|
|
62
|
+
throw new FlowError("INTERNAL_STATE", "repository lifecycle ledger is absent");
|
|
63
|
+
const bind = model.history.find(({ entry }) => entry.kind === "bind")?.entry;
|
|
64
|
+
if (!bind || bind.kind !== "bind")
|
|
65
|
+
throw new FlowError("INTERNAL_STATE", `contract ${input.contractId} is missing folded bind terms`);
|
|
58
66
|
const [deliveryHead, deliveryTree, targetHead, targetTree] = await Promise.all([
|
|
59
|
-
resolveObject(input.root,
|
|
60
|
-
resolveObject(input.root, target.ref, "commit"), resolveObject(input.root, target.ref, "tree"),
|
|
67
|
+
resolveObject(input.root, model.resources.deliveryRef, "commit"), resolveObject(input.root, model.resources.deliveryRef, "tree"),
|
|
68
|
+
resolveObject(input.root, model.pins.target.ref, "commit"), resolveObject(input.root, model.pins.target.ref, "tree"),
|
|
61
69
|
]);
|
|
70
|
+
if (model.pins.target.approvedHead !== null && model.petition.state === "approved" && model.pins.target.approvedHead !== targetHead) {
|
|
71
|
+
throw new FlowError("INTERNAL_STATE", `target ref ${model.pins.target.ref} moved from approved petition pin`);
|
|
72
|
+
}
|
|
62
73
|
return {
|
|
63
|
-
criteria:
|
|
64
|
-
|
|
74
|
+
criteria: auditCriteria(model), scope: [...model.effectiveScope], appendScope: appendedScopeTerms(model), base: model.pins.base,
|
|
75
|
+
lifecycle: model, repository,
|
|
76
|
+
coordinates: { ledger: { head: repository.observedLedgerHead }, delivery: { ref: model.resources.deliveryRef, head: deliveryHead, tree: deliveryTree }, target: { ref: model.pins.target.ref, head: targetHead, tree: targetTree } },
|
|
65
77
|
};
|
|
66
78
|
}
|
|
67
79
|
async function deliveryResidue(cwd) {
|
|
@@ -75,11 +87,6 @@ async function deliveryResidue(cwd) {
|
|
|
75
87
|
continue;
|
|
76
88
|
const xy = field.slice(0, 2);
|
|
77
89
|
const filePath = field.slice(3);
|
|
78
|
-
if (filePath === KEIYAKU_FILE || filePath.startsWith(".keiyaku/recovery/")) {
|
|
79
|
-
if (xy[0] === "R" || xy[0] === "C")
|
|
80
|
-
index += 1;
|
|
81
|
-
continue;
|
|
82
|
-
}
|
|
83
90
|
if (xy === "??")
|
|
84
91
|
untracked.add(filePath);
|
|
85
92
|
else {
|
|
@@ -119,44 +126,88 @@ async function deliveryCoordinate(cwd) {
|
|
|
119
126
|
const [head, tree, residue] = await Promise.all([resolveObject(cwd, "HEAD", "commit"), resolveObject(cwd, "HEAD", "tree"), deliveryResidue(cwd)]);
|
|
120
127
|
return { head, tree, residue, residueFingerprint: await residueFingerprint(cwd, residue) };
|
|
121
128
|
}
|
|
129
|
+
async function checkedOutRef(cwd) {
|
|
130
|
+
try {
|
|
131
|
+
return (await createGit(cwd).raw(["symbolic-ref", "--quiet", "HEAD"])).trim();
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
async function receiptsAfterPinnedFence(input) {
|
|
138
|
+
const fence = input.model.pins.delivery;
|
|
139
|
+
try {
|
|
140
|
+
return (await createGit(input.root).raw(["rev-list", "--reverse", `${fence}..${input.deliveryHead}`]))
|
|
141
|
+
.split(/\r?\n/).filter(Boolean);
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
throw wrapGitError(`rev-list ${fence}..${input.deliveryHead}`, error, input.root);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
122
147
|
async function readReadiness(input) {
|
|
123
|
-
const
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
deliveryResidue(input.deliveryCwd), receiptsAfterFence(input.root, input.frozen.entries, input.frozen.coordinates.delivery.head), readRenewRecoverySession(input.root, input.contractId), readQueueReadModel(input.root),
|
|
148
|
+
const model = input.frozen.lifecycle;
|
|
149
|
+
const [residue, looseCommits, queue, liveProjection, dependencies] = await Promise.all([
|
|
150
|
+
deliveryResidue(input.deliveryCwd), receiptsAfterPinnedFence({ root: input.root, model, deliveryHead: input.frozen.coordinates.delivery.head }), Promise.resolve(deriveQueueReadModel(input.frozen.repository)),
|
|
127
151
|
hasStrandableProjectionGeneration({ cwd: input.root, commissionId: input.contractId, deliveryWorktree: input.deliveryCwd, nowMs: Date.now() }),
|
|
128
|
-
Promise.
|
|
152
|
+
Promise.resolve(model.causal.after.map((id) => {
|
|
153
|
+
const outcome = lookupCurrentContract(input.frozen.repository, id);
|
|
154
|
+
const state = outcome.status === "healthy" ? outcome.model.phase : outcome.status;
|
|
155
|
+
return { id, satisfied: outcome.status === "healthy" && (state === "claimed" || state === "forfeited"), state };
|
|
156
|
+
})),
|
|
129
157
|
]);
|
|
130
158
|
const membership = queueMembership(queue, input.contractId);
|
|
131
|
-
return { deliveryResidue: residue, arc: { open:
|
|
159
|
+
return { deliveryResidue: residue, arc: { open: model.phase === "active" ? model.intent.arc : null, max: model.intent.arc, fence: model.pins.delivery, looseCommits }, rewriteGuard: { liveProjection }, dependencies, petition: { state: model.phase, queue: membership ? { position: membership.position, length: membership.queueLength, head: membership.queueHead, diagnostics: membership.diagnostics.map((item) => item.kind) } : null } };
|
|
132
160
|
}
|
|
133
161
|
export async function pinAuditSnapshot(input) {
|
|
134
162
|
const contractId = input.address.binding.commissionId;
|
|
135
163
|
const root = input.address.repo.stableRoot;
|
|
136
|
-
|
|
137
|
-
return pinLedgerCoordinates({ root, contractId, deliveryRef });
|
|
164
|
+
return pinLedgerCoordinates({ root, contractId });
|
|
138
165
|
}
|
|
139
166
|
export function initialCoordinateRace(input) {
|
|
140
167
|
return { which: "delivery-worktree", pinned: input.frozen.coordinates.delivery, observed: input.observed };
|
|
141
168
|
}
|
|
169
|
+
async function authoritativeDeliveryCwd(root, model) {
|
|
170
|
+
const bind = model.history.find(({ entry }) => entry.kind === "bind")?.entry;
|
|
171
|
+
if (!bind || bind.kind !== "bind")
|
|
172
|
+
throw new FlowError("INTERNAL_STATE", `contract ${model.contract} is missing folded bind terms`);
|
|
173
|
+
if (bind.data.workspace === "here")
|
|
174
|
+
return fs.realpath(root);
|
|
175
|
+
const worktree = model.resources.worktree;
|
|
176
|
+
if (!worktree || worktree.state !== "held" || worktree.place !== bind.data.place) {
|
|
177
|
+
throw new FlowError("INTERNAL_STATE", `contract ${model.contract} has no held worktree matching folded bind place ${bind.data.place}`);
|
|
178
|
+
}
|
|
179
|
+
const registered = await findRegisteredContractWorktreePathFromModel(root, model);
|
|
180
|
+
if (!registered) {
|
|
181
|
+
throw new FlowError("INTERNAL_STATE", `contract ${model.contract} has no Git-registered delivery worktree at folded bind place ${bind.data.place}`);
|
|
182
|
+
}
|
|
183
|
+
return registered;
|
|
184
|
+
}
|
|
142
185
|
export async function pinAuditCoordinates(input) {
|
|
186
|
+
const frozen = await pinAuditSnapshot({ address: input.address });
|
|
187
|
+
return completeAuditCoordinates({ address: input.address, frozen, deliveryCwd: input.deliveryCwd });
|
|
188
|
+
}
|
|
189
|
+
/** Expensive worktree/readiness capture performed after repository admission is released. */
|
|
190
|
+
export async function completeAuditCoordinates(input) {
|
|
143
191
|
const contractId = input.address.binding.commissionId;
|
|
144
192
|
const root = input.address.repo.stableRoot;
|
|
145
|
-
const frozen =
|
|
146
|
-
const deliveryCwd = await
|
|
193
|
+
const frozen = input.frozen;
|
|
194
|
+
const deliveryCwd = await authoritativeDeliveryCwd(root, frozen.lifecycle);
|
|
195
|
+
if (input.deliveryCwd && path.resolve(input.deliveryCwd) !== path.resolve(deliveryCwd)) {
|
|
196
|
+
throw new Error(`observed delivery worktree ${deliveryCwd} does not match expected worktree ${input.deliveryCwd}`);
|
|
197
|
+
}
|
|
147
198
|
const coordinate = await deliveryCoordinate(deliveryCwd);
|
|
148
|
-
|
|
149
|
-
|
|
199
|
+
const branchRef = await checkedOutRef(deliveryCwd);
|
|
200
|
+
if (branchRef !== frozen.coordinates.delivery.ref || coordinate.head !== frozen.coordinates.delivery.head || coordinate.tree !== frozen.coordinates.delivery.tree)
|
|
201
|
+
throw new Error(`registered delivery worktree ${branchRef ?? "detached"} ${coordinate.head}/${coordinate.tree} does not match pinned delivery ref ${frozen.coordinates.delivery.ref} ${frozen.coordinates.delivery.head}/${frozen.coordinates.delivery.tree}`);
|
|
150
202
|
const readiness = await readReadiness({ root, deliveryCwd, contractId, frozen });
|
|
151
203
|
return { ...frozen, deliveryCwd, deliveryCoordinate: coordinate, readiness };
|
|
152
204
|
}
|
|
153
205
|
export async function rereadCoordinateRace(input) {
|
|
154
206
|
const contractId = input.address.binding.commissionId;
|
|
155
207
|
const root = input.address.repo.stableRoot;
|
|
156
|
-
const deliveryRef = `refs/heads/keiyaku/${contractId}`;
|
|
157
208
|
let after;
|
|
158
209
|
try {
|
|
159
|
-
after = await pinLedgerCoordinates({ root, contractId
|
|
210
|
+
after = await pinLedgerCoordinates({ root, contractId });
|
|
160
211
|
}
|
|
161
212
|
catch (error) {
|
|
162
213
|
return { which: "ledger", pinned: input.frozen.coordinates.ledger, observed: asMessage(error) };
|
|
@@ -168,10 +219,14 @@ export async function rereadCoordinateRace(input) {
|
|
|
168
219
|
if (after.coordinates.target.head !== input.frozen.coordinates.target.head || after.coordinates.target.tree !== input.frozen.coordinates.target.tree)
|
|
169
220
|
return { which: "target", pinned: input.frozen.coordinates.target, observed: after.coordinates.target };
|
|
170
221
|
try {
|
|
171
|
-
const deliveryCwd = await
|
|
222
|
+
const deliveryCwd = await authoritativeDeliveryCwd(root, after.lifecycle);
|
|
223
|
+
if (input.deliveryCwd && path.resolve(input.deliveryCwd) !== path.resolve(deliveryCwd)) {
|
|
224
|
+
return { which: "delivery-worktree", pinned: { cwd: input.frozen.deliveryCwd, ...input.frozen.deliveryCoordinate }, observed: { cwd: deliveryCwd, expected: input.deliveryCwd } };
|
|
225
|
+
}
|
|
172
226
|
const observed = await deliveryCoordinate(deliveryCwd);
|
|
173
|
-
|
|
174
|
-
|
|
227
|
+
const branchRef = await checkedOutRef(deliveryCwd);
|
|
228
|
+
if (branchRef !== input.frozen.coordinates.delivery.ref || path.resolve(deliveryCwd) !== path.resolve(input.frozen.deliveryCwd) || observed.head !== input.frozen.deliveryCoordinate.head || observed.tree !== input.frozen.deliveryCoordinate.tree || observed.residueFingerprint !== input.frozen.deliveryCoordinate.residueFingerprint)
|
|
229
|
+
return { which: "delivery-worktree", pinned: { cwd: input.frozen.deliveryCwd, ref: input.frozen.coordinates.delivery.ref, ...input.frozen.deliveryCoordinate }, observed: { cwd: deliveryCwd, ref: branchRef, ...observed } };
|
|
175
230
|
}
|
|
176
231
|
catch (error) {
|
|
177
232
|
return { which: "delivery-worktree", pinned: { cwd: input.frozen.deliveryCwd, ...input.frozen.deliveryCoordinate }, observed: asMessage(error) };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { FlowError, asMessage } from "../../flow-error.js";
|
|
2
2
|
import { scopePathEvidence } from "../scope.js";
|
|
3
|
-
import {
|
|
3
|
+
import { composeVerificationPlan, runVerificationPlan, resolveVerificationPolicy } from "../settlement/verification.js";
|
|
4
4
|
function importLine(line) {
|
|
5
5
|
return /^(?:import\b|export\s+(?:\*|\{)|(?:const|let|var)\s+[^=]+\s*=\s*require\s*\(|[^=]+\brequire\s*\(|[^=]+\bimport\s*\()/.test(line.content.trim());
|
|
6
6
|
}
|
|
@@ -118,32 +118,28 @@ export function presentDiff(input) {
|
|
|
118
118
|
return { budget: { limit: budget, used }, selected: selected.sort((left, right) => left.path.localeCompare(right.path) || (left.hunk ?? -1) - (right.hunk ?? -1)), omissions };
|
|
119
119
|
}
|
|
120
120
|
function fileFacts(diff, scope) { return diff.files.map((file, index) => { const row = scope[index]; return { path: file.path, ...(file.oldPath ? { oldPath: file.oldPath } : {}), status: file.status, stats: { insertions: file.insertions, deletions: file.deletions, binary: file.binary }, matcher: row.matcher, explanations: row.explanations, residue: row.residue, class: row.class }; }); }
|
|
121
|
-
export async function
|
|
122
|
-
let
|
|
121
|
+
export async function resolveAuditVerificationPlan(input) {
|
|
122
|
+
let policy;
|
|
123
123
|
try {
|
|
124
|
-
|
|
124
|
+
policy = await resolveVerificationPolicy(input.cwd);
|
|
125
125
|
}
|
|
126
126
|
catch (error) {
|
|
127
|
-
return { status: "unknown", cause: { code: error instanceof FlowError ? error.code : "INTERNAL_STATE", message: asMessage(error) }
|
|
127
|
+
return { status: "unknown", cause: { code: error instanceof FlowError ? error.code : "INTERNAL_STATE", message: asMessage(error) } };
|
|
128
128
|
}
|
|
129
|
-
|
|
129
|
+
const plan = composeVerificationPlan({ contract: input.contract, repositoryGates: policy.commands, timeoutMs: policy.timeoutMs });
|
|
130
|
+
return { status: "resolved", plan };
|
|
131
|
+
}
|
|
132
|
+
export async function evaluateVerification(input) {
|
|
133
|
+
const plan = input.plan;
|
|
134
|
+
if (plan.length === 0)
|
|
130
135
|
return { status: "not_configured", results: [] };
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
139
|
-
try {
|
|
140
|
-
const execution = await runVerificationCommand(cwd, command);
|
|
141
|
-
results.push({ status: "executed", label: command.label, executor: known.executor, body: known.body, command: command.command, argv, head, exitCode: execution.code, durationMs: execution.durationMs, stdout: execution.stdout, stderr: execution.stderr });
|
|
142
|
-
}
|
|
143
|
-
catch (error) {
|
|
144
|
-
return { status: "unknown", cause: { code: error instanceof FlowError ? error.code : "INTERNAL_STATE", message: asMessage(error) }, results: [] };
|
|
145
|
-
}
|
|
146
|
-
}
|
|
136
|
+
const executed = await runVerificationPlan({ cwd: input.cwd, candidate: { head: input.candidate.head, tree: input.candidate.tree }, plan, signal: input.signal, coordinationKey: input.coordinationKey, coordinationRoot: input.coordinationRoot });
|
|
137
|
+
const results = executed.map((result) => {
|
|
138
|
+
const common = { source: result.source, declaration: result.declaration, label: result.label, executor: result.executor, command: result.command, argv: [result.command, ...result.args], candidate: result.candidate };
|
|
139
|
+
if (result.execution.status === "runner_failed")
|
|
140
|
+
return { ...common, ...result.execution };
|
|
141
|
+
return { status: "executed", ...common, body: result.shell ? result.command : result.args.at(-1) ?? "", exitCode: result.execution.code, durationMs: result.execution.durationMs, stdout: result.execution.stdout, stderr: result.execution.stderr };
|
|
142
|
+
});
|
|
147
143
|
return { status: "evaluated", results };
|
|
148
144
|
}
|
|
149
145
|
export async function collectEvidence(input) {
|
|
@@ -154,5 +150,5 @@ export async function collectEvidence(input) {
|
|
|
154
150
|
return null;
|
|
155
151
|
const diff = input.diff;
|
|
156
152
|
const scope = await evaluateScope({ cwd: input.candidateResult.cwd, frozen: input.frozen, diff });
|
|
157
|
-
return { diff, scope, presentation: presentDiff({ diff, scope, budget: input.diffBudgetBytes, retrievalCoordinates: { from: input.frozen.base, to: input.frozen.coordinates.delivery.head } }), files: fileFacts(diff, scope), verification: await evaluateVerification(input.candidateResult.cwd, candidate.
|
|
153
|
+
return { diff, scope, presentation: presentDiff({ diff, scope, budget: input.diffBudgetBytes, retrievalCoordinates: { from: input.frozen.base, to: input.frozen.coordinates.delivery.head } }), files: fileFacts(diff, scope), verification: input.verificationOverride ?? await evaluateVerification({ cwd: input.candidateResult.cwd, candidate, plan: input.verificationPlan, signal: input.signal, coordinationKey: input.coordinationKey, coordinationRoot: input.coordinationRoot }), candidate };
|
|
158
154
|
}
|
|
@@ -1,27 +1,50 @@
|
|
|
1
|
-
import { createAuditCandidate, disposeAuditCandidate, readAuditCandidateDiff } from "./candidate.js";
|
|
2
|
-
import {
|
|
3
|
-
import { collectEvidence } from "./evidence.js";
|
|
1
|
+
import { createAuditCandidate, disposeAuditCandidate, prepareAuditCandidate, readAuditCandidateDiff } from "./candidate.js";
|
|
2
|
+
import { completeAuditCoordinates, initialCoordinateRace, pinAuditSnapshot, rereadCoordinateRace } from "./coordinates.js";
|
|
3
|
+
import { collectEvidence, resolveAuditVerificationPlan } from "./evidence.js";
|
|
4
4
|
import { collectFindings, emptyRaceReport } from "./report.js";
|
|
5
5
|
import { readinessFromFacts } from "./readiness.js";
|
|
6
6
|
export async function auditContract(input) {
|
|
7
7
|
const contractId = input.address.binding.commissionId;
|
|
8
|
+
const frozenSnapshot = await pinAuditSnapshot({ address: input.address });
|
|
9
|
+
const bind = frozenSnapshot.lifecycle.history.find(({ entry }) => entry.kind === "bind")?.entry;
|
|
10
|
+
const contractVerification = bind?.kind === "bind" ? bind.data.verification : [];
|
|
11
|
+
const admission = {
|
|
12
|
+
frozen: frozenSnapshot,
|
|
13
|
+
contractVerification,
|
|
14
|
+
};
|
|
8
15
|
let frozen;
|
|
9
16
|
try {
|
|
10
|
-
frozen = await
|
|
17
|
+
frozen = await completeAuditCoordinates({ address: input.address, frozen: admission.frozen });
|
|
11
18
|
}
|
|
12
19
|
catch (error) {
|
|
13
|
-
const fallback =
|
|
14
|
-
if (!fallback)
|
|
15
|
-
throw error;
|
|
20
|
+
const fallback = admission.frozen;
|
|
16
21
|
return emptyRaceReport({ contractId, coordinates: fallback.coordinates, terms: { criteria: fallback.criteria, scope: fallback.scope, appendScope: fallback.appendScope }, detail: error instanceof Error ? error.message : String(error), race: initialCoordinateRace({ frozen: fallback, observed: error instanceof Error ? error.message : String(error) }) });
|
|
17
22
|
}
|
|
18
23
|
const candidateResult = await createAuditCandidate({ root: input.address.repo.stableRoot, contractId, frozen });
|
|
19
24
|
let evidence = null;
|
|
20
25
|
let verification;
|
|
26
|
+
let candidatePreparation = { status: "not-required", reason: "candidate_unavailable" };
|
|
21
27
|
try {
|
|
28
|
+
let verificationPlan = [];
|
|
29
|
+
let verificationOverride;
|
|
30
|
+
if (candidateResult.candidate) {
|
|
31
|
+
const planResolution = await resolveAuditVerificationPlan({ cwd: candidateResult.cwd, contract: admission.contractVerification });
|
|
32
|
+
if (planResolution.status === "unknown") {
|
|
33
|
+
candidatePreparation = { status: "not-required", reason: "verification_plan_unavailable" };
|
|
34
|
+
verificationOverride = { status: "unknown", cause: planResolution.cause, results: [] };
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
verificationPlan = planResolution.plan;
|
|
38
|
+
const lifecycleBind = frozen.lifecycle.history.find(({ entry }) => entry.kind === "bind")?.entry;
|
|
39
|
+
candidatePreparation = await prepareAuditCandidate({ candidateResult, verificationRequired: verificationPlan.length > 0, plan: lifecycleBind?.kind === "bind" && lifecycleBind.data.workspace === "worktree" ? lifecycleBind.data.bootstrap : undefined });
|
|
40
|
+
if (candidatePreparation.status === "failed") {
|
|
41
|
+
verificationOverride = { status: "not_evaluated", reason: "candidate_preparation_failed", results: [] };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
22
45
|
const diff = await readAuditCandidateDiff(candidateResult);
|
|
23
|
-
evidence = await collectEvidence({ candidateResult, frozen, diff, diffBudgetBytes: input.diffBudgetBytes ?? 32_000 });
|
|
24
|
-
verification = evidence?.verification ?? { status: "not_evaluated", reason: candidateResult.rehearsal.status === "conflict" ? "renew_conflict" : "renew_unknown", results: [] };
|
|
46
|
+
evidence = await collectEvidence({ candidateResult, frozen, diff, diffBudgetBytes: input.diffBudgetBytes ?? 32_000, verificationPlan, signal: input.signal, coordinationKey: JSON.stringify({ repository: input.address.repo.stableRoot, candidate: candidateResult.candidate, preparation: bind?.kind === "bind" && bind.data.workspace === "worktree" ? bind.data.bootstrap : null, plan: verificationPlan }), coordinationRoot: input.address.repo.stableRoot, ...(verificationOverride ? { verificationOverride } : {}) });
|
|
47
|
+
verification = evidence?.verification ?? verificationOverride ?? { status: "not_evaluated", reason: candidateResult.rehearsal.status === "conflict" ? "renew_conflict" : "renew_unknown", results: [] };
|
|
25
48
|
await input.afterEvidence?.();
|
|
26
49
|
}
|
|
27
50
|
finally {
|
|
@@ -37,6 +60,6 @@ export async function auditContract(input) {
|
|
|
37
60
|
return emptyRaceReport({ contractId, coordinates: frozen.coordinates, terms: { criteria: frozen.criteria, scope: frozen.scope, appendScope: frozen.appendScope }, detail, race });
|
|
38
61
|
}
|
|
39
62
|
const readiness = readinessFromFacts(frozen.readiness);
|
|
40
|
-
const findings = collectFindings({ contractId, deliveryCwd: frozen.deliveryCwd, rehearsal: candidateResult.rehearsal, scope: evidence?.scope ?? null, verification, readiness });
|
|
41
|
-
return { contract: contractId, coordinates: frozen.coordinates, terms: { criteria: frozen.criteria, scope: frozen.scope, appendScope: frozen.appendScope }, rehearsal: candidateResult.rehearsal, candidate: evidence ? (evidence.candidate.rewritten ? { kind: "renew-rehearsal", base: evidence.candidate.base, head: evidence.candidate.head, tree: evidence.candidate.tree, deliveryHead: frozen.coordinates.delivery.head, targetHead: frozen.coordinates.target.head } : { kind: "delivery", base: evidence.candidate.base, head: evidence.candidate.head, tree: evidence.candidate.tree }) : null, diff: evidence?.diff ?? null, presentation: evidence?.presentation ?? null, files: evidence?.files ?? null, scope: evidence?.scope ?? null, verification, readiness, findings, counts: { files: evidence?.files.length ?? 0, selected: evidence?.presentation.selected.length ?? 0, omitted: evidence?.presentation.omissions.length ?? 0, inScope: evidence?.scope.filter((row) => row.class === "in-scope").length ?? 0, downgraded: evidence?.scope.filter((row) => row.class === "downgraded").length ?? 0, violated: evidence?.scope.filter((row) => row.class === "violated").length ?? 0, findings: findings.length } };
|
|
63
|
+
const findings = collectFindings({ contractId, deliveryCwd: frozen.deliveryCwd, rehearsal: candidateResult.rehearsal, scope: evidence?.scope ?? null, candidatePreparation, verification, readiness });
|
|
64
|
+
return { contract: contractId, coordinates: frozen.coordinates, terms: { criteria: frozen.criteria, scope: frozen.scope, appendScope: frozen.appendScope }, rehearsal: candidateResult.rehearsal, candidate: evidence ? (evidence.candidate.rewritten ? { kind: "renew-rehearsal", base: evidence.candidate.base, head: evidence.candidate.head, tree: evidence.candidate.tree, deliveryHead: frozen.coordinates.delivery.head, targetHead: frozen.coordinates.target.head } : { kind: "delivery", base: evidence.candidate.base, head: evidence.candidate.head, tree: evidence.candidate.tree }) : null, diff: evidence?.diff ?? null, presentation: evidence?.presentation ?? null, files: evidence?.files ?? null, scope: evidence?.scope ?? null, candidatePreparation, verification, readiness, findings, counts: { files: evidence?.files.length ?? 0, selected: evidence?.presentation.selected.length ?? 0, omitted: evidence?.presentation.omissions.length ?? 0, inScope: evidence?.scope.filter((row) => row.class === "in-scope").length ?? 0, downgraded: evidence?.scope.filter((row) => row.class === "downgraded").length ?? 0, violated: evidence?.scope.filter((row) => row.class === "violated").length ?? 0, findings: findings.length } };
|
|
42
65
|
}
|
|
@@ -9,12 +9,14 @@ export function collectFindings(input) {
|
|
|
9
9
|
findings.push({ code: "RENEW_UNKNOWN", detail: `${input.rehearsal.cause.code}: ${input.rehearsal.cause.message}`, nextCommand: `${selector} audit` });
|
|
10
10
|
for (const row of input.scope?.filter((item) => item.class === "violated") ?? [])
|
|
11
11
|
findings.push({ code: "SCOPE_RESIDUE", detail: `${row.path}: ${row.residue.length} unexplained changes`, nextCommand: `${selector} amend --scope-add ${JSON.stringify(row.path)}` });
|
|
12
|
+
if (input.candidatePreparation.status === "failed")
|
|
13
|
+
findings.push({ code: "CANDIDATE_PREPARATION_FAILED", detail: `${input.candidatePreparation.cause.code}: ${input.candidatePreparation.cause.message}`, nextCommand: `${selector} audit` });
|
|
12
14
|
if (input.verification.status === "evaluated") {
|
|
13
15
|
for (const result of input.verification.results) {
|
|
14
16
|
if (result.status === "executed" && result.exitCode !== 0)
|
|
15
17
|
findings.push({ code: "VERIFICATION_FAILED", detail: `${result.label} exited ${result.exitCode}`, nextCommand: result.command });
|
|
16
|
-
if (result.status === "
|
|
17
|
-
findings.push({ code: "
|
|
18
|
+
if (result.status === "runner_failed")
|
|
19
|
+
findings.push({ code: "VERIFICATION_UNKNOWN", detail: `${result.source} ${result.label}: ${result.cause.code}: ${result.cause.message}`, nextCommand: result.command });
|
|
18
20
|
}
|
|
19
21
|
}
|
|
20
22
|
const residue = input.readiness.deliveryResidue;
|
|
@@ -22,8 +24,6 @@ export function collectFindings(input) {
|
|
|
22
24
|
findings.push({ code: "DELIVERY_RESIDUE", detail: `registered delivery worktree has uncommitted residue at ${input.deliveryCwd}`, nextCommand: `git -C ${JSON.stringify(input.deliveryCwd)} status --short` });
|
|
23
25
|
if (input.readiness.arc.max > 0 && input.readiness.arc.open === null && input.readiness.arc.looseCommits.length > 0)
|
|
24
26
|
findings.push({ code: "LOOSE_COMMITS", detail: `${input.readiness.arc.looseCommits.length} loose commits after fence`, nextCommand: `printf '# Continue\\n\\n## Objective\\nReceipt loose commits.\\n\\n## Brief\\nContinue delivery.\\n' | ${selector} arc` });
|
|
25
|
-
if (input.readiness.renewSession.open)
|
|
26
|
-
findings.push({ code: "RENEW_SESSION_OPEN", detail: "renew recovery session is open", nextCommand: `${selector} renew --continue` });
|
|
27
27
|
if (input.readiness.rewriteGuard.liveProjection)
|
|
28
28
|
findings.push({ code: "LIVE_REWRITE_GUARD", detail: "a live projection owns the delivery worktree", nextCommand: "keiyaku status" });
|
|
29
29
|
for (const dependency of input.readiness.dependencies.filter((item) => !item.satisfied))
|
|
@@ -34,5 +34,5 @@ export function collectFindings(input) {
|
|
|
34
34
|
}
|
|
35
35
|
export function emptyRaceReport(input) {
|
|
36
36
|
const findings = [{ code: "COORDINATE_RACE", detail: input.detail, nextCommand: `keiyaku @${input.contractId} audit`, race: input.race }];
|
|
37
|
-
return { contract: input.contractId, coordinates: input.coordinates, terms: input.terms, rehearsal: { status: "unknown", cause: { code: "COORDINATE_RACE", message: input.detail } }, candidate: null, diff: null, presentation: null, files: null, scope: null, verification: { status: "not_evaluated", reason: "coordinate_race", results: [] }, readiness: null, findings, counts: { files: 0, selected: 0, omitted: 0, inScope: 0, downgraded: 0, violated: 0, findings: 1 } };
|
|
37
|
+
return { contract: input.contractId, coordinates: input.coordinates, terms: input.terms, rehearsal: { status: "unknown", cause: { code: "COORDINATE_RACE", message: input.detail } }, candidate: null, diff: null, presentation: null, files: null, scope: null, candidatePreparation: { status: "not-required", reason: "coordinate_race" }, verification: { status: "not_evaluated", reason: "coordinate_race", results: [] }, readiness: null, findings, counts: { files: 0, selected: 0, omitted: 0, inScope: 0, downgraded: 0, violated: 0, findings: 1 } };
|
|
38
38
|
}
|