@astrosheep/keiyaku 2.9.9 → 2.9.11
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 +93 -174
- 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 +2 -1
- 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 +6 -1
- 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 +3 -2
- package/build/cli/render/projection-history.js +64 -0
- package/build/cli/render/shared.js +23 -9
- package/build/cli/render/status.js +9 -16
- package/build/cli/render/task.js +51 -26
- package/build/cli/render/terminal-failure.js +3 -1
- package/build/cli/render/wait.js +3 -5
- package/build/cli/subagent-guard.js +1 -1
- package/build/cli/types.js +1 -1
- package/build/config/env-keys.js +2 -0
- package/build/config/env.js +23 -0
- 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 +6 -5
- package/build/core/projection/index.js +5 -4
- package/build/core/projection/projection-activity.js +27 -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 +21 -358
- 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 +114 -119
- 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
|
@@ -1,14 +1,61 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
2
|
import * as fs from "node:fs/promises";
|
|
3
3
|
import { FlowError } from "../flow-error.js";
|
|
4
|
-
import { createGit, wrapGitError } from "../git/core.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { deriveContractState, isTerminalState } from "./status/lifecycle.js";
|
|
4
|
+
import { createGit, runGitProcess, wrapGitError } from "../git/core.js";
|
|
5
|
+
import { resolveLocalBranchHead } from "../git/refs.js";
|
|
6
|
+
import { readCurrentContractLifecycle } from "./repository-ledger/read-model.js";
|
|
8
7
|
import { normalizeFilesystemCoordinate } from "../fs/path-coordinate.js";
|
|
9
8
|
import { gitCommonRootPath, resolveGitCommonRoot } from "../git/path-coordinate.js";
|
|
10
|
-
|
|
11
|
-
return
|
|
9
|
+
function record(value) {
|
|
10
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
11
|
+
? value
|
|
12
|
+
: undefined;
|
|
13
|
+
}
|
|
14
|
+
/** Reconstruct the public terminal-cleanup fact from its independent replay receipts. */
|
|
15
|
+
export function terminalCleanupFacts(worktreeValue, deliveryRefValue) {
|
|
16
|
+
const worktree = record(worktreeValue);
|
|
17
|
+
const delivery = record(deliveryRefValue);
|
|
18
|
+
if (!worktree || !Array.isArray(worktree.absent) || !delivery)
|
|
19
|
+
return undefined;
|
|
20
|
+
const absent = worktree.absent.filter((item) => item === "worktree");
|
|
21
|
+
const base = {
|
|
22
|
+
absent,
|
|
23
|
+
...(typeof worktree.removedWorktreePath === "string" ? { removedWorktreePath: worktree.removedWorktreePath } : {}),
|
|
24
|
+
};
|
|
25
|
+
const failure = record(worktree.worktreeRemoveFailure);
|
|
26
|
+
if (failure && typeof failure.path === "string" && typeof failure.reason === "string") {
|
|
27
|
+
base.worktreeRemoveFailure = { path: failure.path, reason: failure.reason };
|
|
28
|
+
}
|
|
29
|
+
if (delivery.status === "aligned"
|
|
30
|
+
&& typeof delivery.ref === "string"
|
|
31
|
+
&& delivery.expected === "absent"
|
|
32
|
+
&& delivery.observed === "absent") {
|
|
33
|
+
return { ...base, deliveryRef: { status: "aligned", ref: delivery.ref, expected: "absent", observed: "absent" } };
|
|
34
|
+
}
|
|
35
|
+
const mismatch = record(delivery.mismatch);
|
|
36
|
+
const observed = record(mismatch?.observed);
|
|
37
|
+
if (delivery.status === "mismatch"
|
|
38
|
+
&& mismatch?.kind === "projected-surface-mismatch"
|
|
39
|
+
&& mismatch.surface === "delivery-ref"
|
|
40
|
+
&& typeof mismatch.ref === "string"
|
|
41
|
+
&& mismatch.expected === "absent"
|
|
42
|
+
&& observed?.state === "present"
|
|
43
|
+
&& typeof observed.head === "string") {
|
|
44
|
+
return {
|
|
45
|
+
...base,
|
|
46
|
+
deliveryRef: {
|
|
47
|
+
status: "mismatch",
|
|
48
|
+
mismatch: {
|
|
49
|
+
kind: "projected-surface-mismatch",
|
|
50
|
+
surface: "delivery-ref",
|
|
51
|
+
ref: mismatch.ref,
|
|
52
|
+
expected: "absent",
|
|
53
|
+
observed: { state: "present", head: observed.head },
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
return undefined;
|
|
12
59
|
}
|
|
13
60
|
function parseWorktreeListPorcelainZ(output) {
|
|
14
61
|
const listings = [];
|
|
@@ -28,7 +75,16 @@ function parseWorktreeListPorcelainZ(output) {
|
|
|
28
75
|
listings.push(current);
|
|
29
76
|
return listings;
|
|
30
77
|
}
|
|
31
|
-
async function listWorktrees(cwd) {
|
|
78
|
+
async function listWorktrees(cwd, signal) {
|
|
79
|
+
if (signal) {
|
|
80
|
+
const result = await runGitProcess(cwd, ["worktree", "list", "--porcelain", "-z"], { signal });
|
|
81
|
+
if (result.error || result.status !== 0) {
|
|
82
|
+
if (result.error?.name === "AbortError")
|
|
83
|
+
throw result.error;
|
|
84
|
+
throw wrapGitError("worktree list --porcelain -z", result.error ?? new Error(result.stderr || `exit ${String(result.status)}`), cwd);
|
|
85
|
+
}
|
|
86
|
+
return parseWorktreeListPorcelainZ(result.stdout);
|
|
87
|
+
}
|
|
32
88
|
const git = createGit(cwd);
|
|
33
89
|
try {
|
|
34
90
|
return parseWorktreeListPorcelainZ(await git.raw(["worktree", "list", "--porcelain", "-z"]));
|
|
@@ -80,16 +136,19 @@ function contractWorktreePathFromStableRoot(stableRoot, contractId, bind) {
|
|
|
80
136
|
return path.join(stableRoot, ".keiyaku", "wt", bind?.data.place ?? contractId);
|
|
81
137
|
}
|
|
82
138
|
export async function contractWorktreePath(cwd, contractId) {
|
|
83
|
-
const
|
|
84
|
-
const
|
|
85
|
-
return
|
|
139
|
+
const stableRoot = await stableRepoRoot(cwd);
|
|
140
|
+
const outcome = await readCurrentContractLifecycle(stableRoot, contractId);
|
|
141
|
+
return path.join(stableRoot, ".keiyaku", "wt", outcome.status === "healthy" || outcome.status === "diseased"
|
|
142
|
+
? outcome.model.resources.worktree?.place ?? contractId
|
|
143
|
+
: contractId);
|
|
86
144
|
}
|
|
87
145
|
export async function requireContractWorktreePath(cwd, contractId) {
|
|
88
|
-
const
|
|
89
|
-
|
|
146
|
+
const stableRoot = await stableRepoRoot(cwd);
|
|
147
|
+
const outcome = await readCurrentContractLifecycle(stableRoot, contractId);
|
|
148
|
+
if (outcome.status !== "healthy" || !outcome.model.resources.worktree) {
|
|
90
149
|
throw new FlowError("INTERNAL_STATE", `contract ${contractId} not found`);
|
|
91
150
|
}
|
|
92
|
-
return
|
|
151
|
+
return path.join(stableRoot, ".keiyaku", "wt", outcome.model.resources.worktree.place);
|
|
93
152
|
}
|
|
94
153
|
async function findContractWorktree(cwd, contractId) {
|
|
95
154
|
const expectedBranch = `refs/heads/keiyaku/${contractId}`;
|
|
@@ -108,21 +167,53 @@ export async function findContractWorktreePath(cwd, contractId) {
|
|
|
108
167
|
}
|
|
109
168
|
export async function findRegisteredContractWorktreePath(cwd, contractId) {
|
|
110
169
|
const stableRoot = await stableRepoRoot(cwd);
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
if (!bind || bind.data.workspace !== "worktree")
|
|
170
|
+
const outcome = await readCurrentContractLifecycle(stableRoot, contractId);
|
|
171
|
+
if (outcome.status !== "healthy" || outcome.model.resources.worktree?.state !== "held")
|
|
114
172
|
return undefined;
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
173
|
+
return findRegisteredContractWorktreePathFromModel(stableRoot, outcome.model);
|
|
174
|
+
}
|
|
175
|
+
export async function findRegisteredContractWorktreePathFromModel(stableRoot, model) {
|
|
176
|
+
const worktree = model.resources.worktree;
|
|
177
|
+
if (!worktree)
|
|
120
178
|
return undefined;
|
|
179
|
+
const expected = path.join(stableRoot, ".keiyaku", "wt", worktree.place);
|
|
180
|
+
for (const registered of await listWorktrees(stableRoot)) {
|
|
181
|
+
if (registered.branch !== model.resources.deliveryRef)
|
|
182
|
+
continue;
|
|
183
|
+
try {
|
|
184
|
+
if (normalizeFilesystemCoordinate(await fs.realpath(registered.path)) === normalizeFilesystemCoordinate(await fs.realpath(expected))) {
|
|
185
|
+
return await fs.realpath(registered.path);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
catch { /* stale registration is not lifecycle evidence */ }
|
|
121
189
|
}
|
|
190
|
+
return undefined;
|
|
191
|
+
}
|
|
192
|
+
async function findRegisteredContractWorktreePathFromBind(stableRoot, contractId, _bind) {
|
|
193
|
+
const expectedBranch = `refs/heads/keiyaku/${contractId}`;
|
|
122
194
|
for (const worktree of await listWorktrees(stableRoot)) {
|
|
195
|
+
let ownsBranch = worktree.branch === expectedBranch;
|
|
196
|
+
if (!ownsBranch && !worktree.branch) {
|
|
197
|
+
for (const statePath of ["rebase-merge/head-name", "rebase-apply/head-name"]) {
|
|
198
|
+
try {
|
|
199
|
+
const gitPath = (await createGit(worktree.path).raw(["rev-parse", "--git-path", statePath])).trim();
|
|
200
|
+
if ((await fs.readFile(gitPath, "utf8")).trim() === expectedBranch) {
|
|
201
|
+
ownsBranch = true;
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
// No owned rebase coordinate at this registered worktree.
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
if (!ownsBranch)
|
|
211
|
+
continue;
|
|
123
212
|
try {
|
|
124
|
-
|
|
125
|
-
|
|
213
|
+
const dotGit = await fs.stat(path.join(worktree.path, ".git"));
|
|
214
|
+
if (dotGit.isDirectory())
|
|
215
|
+
continue;
|
|
216
|
+
return await fs.realpath(worktree.path);
|
|
126
217
|
}
|
|
127
218
|
catch {
|
|
128
219
|
// A stale Git registration is not an engine-owned worktree fact.
|
|
@@ -130,12 +221,21 @@ export async function findRegisteredContractWorktreePath(cwd, contractId) {
|
|
|
130
221
|
}
|
|
131
222
|
return undefined;
|
|
132
223
|
}
|
|
133
|
-
/**
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
224
|
+
/** Prove the sole carrier target from stable-root, bind, and Git registration facts. */
|
|
225
|
+
export async function resolveContractCarrierTarget(stableRoot, contractId, bind) {
|
|
226
|
+
if (bind.data.workspace === "here") {
|
|
227
|
+
const root = await fs.realpath(stableRoot);
|
|
228
|
+
return { status: "available", root, path: path.join(root, ".keiyaku", "KEIYAKU.md"), workspace: "here" };
|
|
229
|
+
}
|
|
230
|
+
const root = await findRegisteredContractWorktreePathFromBind(stableRoot, contractId, bind);
|
|
231
|
+
if (!root) {
|
|
232
|
+
return {
|
|
233
|
+
status: "unavailable",
|
|
234
|
+
workspace: "worktree",
|
|
235
|
+
reason: `contract ${contractId} has no verified registered engine worktree`,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
return { status: "available", root, path: path.join(root, ".keiyaku", "KEIYAKU.md"), workspace: "worktree" };
|
|
139
239
|
}
|
|
140
240
|
/**
|
|
141
241
|
* Resolve the contract id for an engine-owned bound worktree containing cwd.
|
|
@@ -144,10 +244,29 @@ export async function resolveContractDeliveryCwd(cwd, contractId) {
|
|
|
144
244
|
*/
|
|
145
245
|
async function contractIdForEngineOwnedWorktree(cwd, options) {
|
|
146
246
|
const stableRoot = options.stableRoot ?? await stableRepoRoot(cwd);
|
|
247
|
+
const candidate = await contractIdentityFromWorktreeWitness(cwd, stableRoot);
|
|
248
|
+
if (!candidate)
|
|
249
|
+
return undefined;
|
|
250
|
+
const outcome = await readCurrentContractLifecycle(stableRoot, candidate.contractId);
|
|
251
|
+
if (outcome.status !== "healthy" || !outcome.model.resources.worktree)
|
|
252
|
+
return undefined;
|
|
253
|
+
if (options.requireActive && ["claimed", "forfeited"].includes(outcome.model.phase))
|
|
254
|
+
return undefined;
|
|
255
|
+
let expectedWorktree;
|
|
256
|
+
try {
|
|
257
|
+
expectedWorktree = await fs.realpath(path.join(stableRoot, ".keiyaku", "wt", outcome.model.resources.worktree.place));
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
return undefined;
|
|
261
|
+
}
|
|
262
|
+
return candidate.path === expectedWorktree ? candidate.contractId : undefined;
|
|
263
|
+
}
|
|
264
|
+
/** Resolve only Git's local registered-worktree witness; lifecycle facts must verify it separately. */
|
|
265
|
+
export async function contractIdentityFromWorktreeWitness(cwd, stableRoot, signal) {
|
|
147
266
|
const canonicalCwd = await fs.realpath(cwd);
|
|
267
|
+
const branchPrefix = "refs/heads/keiyaku/";
|
|
148
268
|
const candidates = [];
|
|
149
|
-
for (const worktree of await listWorktrees(stableRoot)) {
|
|
150
|
-
const branchPrefix = "refs/heads/keiyaku/";
|
|
269
|
+
for (const worktree of await listWorktrees(stableRoot, signal)) {
|
|
151
270
|
if (!worktree.branch?.startsWith(branchPrefix))
|
|
152
271
|
continue;
|
|
153
272
|
let canonicalWorktree;
|
|
@@ -162,27 +281,10 @@ async function contractIdForEngineOwnedWorktree(cwd, options) {
|
|
|
162
281
|
candidates.push({
|
|
163
282
|
contractId: worktree.branch.slice(branchPrefix.length),
|
|
164
283
|
path: canonicalWorktree,
|
|
284
|
+
branch: worktree.branch,
|
|
165
285
|
});
|
|
166
286
|
}
|
|
167
|
-
|
|
168
|
-
if (!candidate)
|
|
169
|
-
return undefined;
|
|
170
|
-
const ledger = await readLedgerSnapshot(stableRoot, candidate.contractId);
|
|
171
|
-
if (!ledger)
|
|
172
|
-
return undefined;
|
|
173
|
-
if (options.requireActive && isTerminalState(deriveContractState(ledger.entries)))
|
|
174
|
-
return undefined;
|
|
175
|
-
const bind = findBindEntry(ledger.entries);
|
|
176
|
-
if (!bind || bind.data.workspace !== "worktree")
|
|
177
|
-
return undefined;
|
|
178
|
-
let expectedWorktree;
|
|
179
|
-
try {
|
|
180
|
-
expectedWorktree = await fs.realpath(contractWorktreePathFromStableRoot(stableRoot, candidate.contractId, bind));
|
|
181
|
-
}
|
|
182
|
-
catch {
|
|
183
|
-
return undefined;
|
|
184
|
-
}
|
|
185
|
-
return candidate.path === expectedWorktree ? candidate.contractId : undefined;
|
|
287
|
+
return candidates.sort((left, right) => right.path.length - left.path.length || left.contractId.localeCompare(right.contractId))[0];
|
|
186
288
|
}
|
|
187
289
|
/**
|
|
188
290
|
* Resolve the contract bound by an engine-owned worktree containing cwd,
|
|
@@ -196,12 +298,48 @@ export async function contractBoundByEngineWorktree(cwd, stableRoot) {
|
|
|
196
298
|
export async function activeContractBoundByEngineWorktree(cwd, stableRoot) {
|
|
197
299
|
return contractIdForEngineOwnedWorktree(cwd, { requireActive: true, stableRoot });
|
|
198
300
|
}
|
|
199
|
-
|
|
301
|
+
/**
|
|
302
|
+
* Verify a worktree binding against a folded current-ledger model. Git's
|
|
303
|
+
* registration is only a physical witness here; it cannot create lifecycle
|
|
304
|
+
* state or select a contract absent from the supplied read model.
|
|
305
|
+
*/
|
|
306
|
+
export async function currentContractBoundByWorktreeWitness(input) {
|
|
307
|
+
let canonicalCwd;
|
|
308
|
+
try {
|
|
309
|
+
canonicalCwd = await fs.realpath(input.cwd);
|
|
310
|
+
}
|
|
311
|
+
catch {
|
|
312
|
+
return undefined;
|
|
313
|
+
}
|
|
314
|
+
const registeredWorktrees = await listWorktrees(input.stableRoot);
|
|
315
|
+
const candidates = [];
|
|
316
|
+
for (const model of input.contracts) {
|
|
317
|
+
const worktree = model.resources.worktree;
|
|
318
|
+
if (!worktree || worktree.state !== "held")
|
|
319
|
+
continue;
|
|
320
|
+
const expectedPath = path.join(input.stableRoot, ".keiyaku", "wt", worktree.place);
|
|
321
|
+
let canonicalExpectedPath;
|
|
322
|
+
try {
|
|
323
|
+
canonicalExpectedPath = await fs.realpath(expectedPath);
|
|
324
|
+
}
|
|
325
|
+
catch {
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
if (!worktreeContainsCoordinate(canonicalExpectedPath, canonicalCwd))
|
|
329
|
+
continue;
|
|
330
|
+
const registered = registeredWorktrees.some((candidate) => candidate.branch === model.resources.deliveryRef &&
|
|
331
|
+
normalizeFilesystemCoordinate(candidate.path) === normalizeFilesystemCoordinate(canonicalExpectedPath));
|
|
332
|
+
if (registered)
|
|
333
|
+
candidates.push({ contract: model.contract, path: canonicalExpectedPath });
|
|
334
|
+
}
|
|
335
|
+
candidates.sort((left, right) => right.path.length - left.path.length || left.contract.localeCompare(right.contract));
|
|
336
|
+
return candidates[0]?.contract;
|
|
337
|
+
}
|
|
338
|
+
/** Remove only the derived linked worktree. Authoritative refs are outside this adapter. */
|
|
339
|
+
export async function cleanupContractWorkspace(cwd, lifecycle) {
|
|
200
340
|
const stableRoot = await stableRepoRoot(cwd);
|
|
201
341
|
const git = createGit(stableRoot);
|
|
202
|
-
const
|
|
203
|
-
const worktree = await findContractWorktree(stableRoot, contractId);
|
|
204
|
-
const worktreePath = worktree.path;
|
|
342
|
+
const worktreePath = await findRegisteredContractWorktreePathFromModel(stableRoot, lifecycle);
|
|
205
343
|
const facts = { absent: [] };
|
|
206
344
|
if (worktreePath) {
|
|
207
345
|
try {
|
|
@@ -215,28 +353,23 @@ export async function cleanupContractWorkspace(cwd, contractId) {
|
|
|
215
353
|
else {
|
|
216
354
|
facts.absent.push("worktree");
|
|
217
355
|
}
|
|
218
|
-
if (worktree.primary) {
|
|
219
|
-
facts.absent.push("branch");
|
|
220
|
-
return facts;
|
|
221
|
-
}
|
|
222
|
-
let branchRef;
|
|
223
|
-
try {
|
|
224
|
-
branchRef = (await git.raw(["for-each-ref", "--format=%(refname)", `refs/heads/${branch}`])).trim();
|
|
225
|
-
}
|
|
226
|
-
catch (error) {
|
|
227
|
-
throw wrapGitError(`for-each-ref refs/heads/${branch}`, error, stableRoot);
|
|
228
|
-
}
|
|
229
|
-
if (branchRef !== `refs/heads/${branch}`) {
|
|
230
|
-
facts.absent.push("branch");
|
|
231
|
-
}
|
|
232
|
-
else {
|
|
233
|
-
try {
|
|
234
|
-
await git.raw(["branch", "-D", branch]);
|
|
235
|
-
facts.deletedBranch = branch;
|
|
236
|
-
}
|
|
237
|
-
catch (error) {
|
|
238
|
-
facts.branchDeleteFailure = { name: branch, reason: wrapGitError(`branch -D ${branch}`, error, stableRoot).message };
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
356
|
return facts;
|
|
242
357
|
}
|
|
358
|
+
/** Observe the terminal transaction's expected delivery-ref absence without reconciling it. */
|
|
359
|
+
export async function observeTerminalDeliveryRef(cwd, contractId) {
|
|
360
|
+
const stableRoot = await stableRepoRoot(cwd);
|
|
361
|
+
const ref = `refs/heads/keiyaku/${contractId}`;
|
|
362
|
+
const observed = await resolveLocalBranchHead(stableRoot, ref);
|
|
363
|
+
if (!observed)
|
|
364
|
+
return { status: "aligned", ref, expected: "absent", observed: "absent" };
|
|
365
|
+
return {
|
|
366
|
+
status: "mismatch",
|
|
367
|
+
mismatch: {
|
|
368
|
+
kind: "projected-surface-mismatch",
|
|
369
|
+
surface: "delivery-ref",
|
|
370
|
+
ref,
|
|
371
|
+
expected: "absent",
|
|
372
|
+
observed: { state: "present", head: observed.head },
|
|
373
|
+
},
|
|
374
|
+
};
|
|
375
|
+
}
|
package/build/flow-error.js
CHANGED
|
@@ -64,6 +64,8 @@ export function resolveFlowErrorHints(code, context, facts) {
|
|
|
64
64
|
return [`Next: open an arc with \`${context.arcToolName}\`, then use \`${context.renewToolName}\` when delivery must follow the current base, or \`${context.closeToolName}\` to settle it.`];
|
|
65
65
|
case "CONTRACT_AMBIGUOUS":
|
|
66
66
|
return ["Address a specific contract workspace, or use `call --bare` to run without contract attachment."];
|
|
67
|
+
case "TASK_AMBIGUOUS":
|
|
68
|
+
return ["Pass one complete task coordinate from the listed candidates."];
|
|
67
69
|
case "UNKNOWN_COMMAND":
|
|
68
70
|
return ["Run `keiyaku --help` to inspect available commands."];
|
|
69
71
|
case "EMPTY_PARAM":
|
|
@@ -77,13 +79,11 @@ export function resolveFlowErrorHints(code, context, facts) {
|
|
|
77
79
|
case "MISSING_PROTOCOL_FILES":
|
|
78
80
|
return ["Required protocol file is missing (`KEIYAKU.md`)."];
|
|
79
81
|
case "CLOSE_MERGE_CONFLICT":
|
|
80
|
-
return [
|
|
82
|
+
return [];
|
|
81
83
|
case "RENEW_COORDINATE_DRIFT":
|
|
82
84
|
return [`Restore the delivery to its ledger-recorded history with Git reflog, then retry \`keiyaku ${context.renewToolName}\`.`];
|
|
83
|
-
case "
|
|
84
|
-
return [`
|
|
85
|
-
case "RENEW_SESSION_NOT_FOUND":
|
|
86
|
-
return [`No recovery session is open; run \`keiyaku ${context.renewToolName}\` normally.`];
|
|
85
|
+
case "RENEW_SCRATCH_NOT_FOUND":
|
|
86
|
+
return [`No renew conflict scratch is open; run \`keiyaku ${context.renewToolName}\` normally.`];
|
|
87
87
|
case "CLOSE_QUALITY_GATE_FAILED":
|
|
88
88
|
return [`CLAIM denied: a configured verification command failed. Use \`${context.arcToolName}\` after addressing the failure before petitioning again.`];
|
|
89
89
|
case "INVALID_BRANCH_TITLE":
|
|
@@ -95,9 +95,9 @@ export function resolveFlowErrorHints(code, context, facts) {
|
|
|
95
95
|
case "FROM_DRAFT_NOT_FOUND":
|
|
96
96
|
return ["The path in `from_draft` does not exist. `from_draft` must reference an existing file path (absolute or relative to `cwd`)."];
|
|
97
97
|
case "INVALID_KEIYAKU_DRAFT":
|
|
98
|
-
return ["Invalid draft/input. Use a `# <title>` heading with non-empty `## Objective
|
|
98
|
+
return ["Invalid draft/input. Use a `# <title>` heading with non-empty `## Context`, `## Objective`, `## Design`, `## Scope`, `## Criteria`, and `## Verification` sections (or the equivalent shape in `from_draft`)."];
|
|
99
99
|
case "BIND_TASK_INCOMPLETE":
|
|
100
|
-
return ["Task supplies
|
|
100
|
+
return ["Task supplies title/objective defaults; pipe only the missing sections as partial contract Markdown on stdin."];
|
|
101
101
|
case "KEIYAKU_FILE_EXISTS":
|
|
102
102
|
return [`\`${context.bindToolName}\` requires a clean slate. \`KEIYAKU.md\` must be removed before starting.`];
|
|
103
103
|
case "DETACHED_HEAD":
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// Generated into build output by scripts/build.mjs
|
|
2
|
-
export const VERSION = "2.9.
|
|
3
|
-
export const GIT_HASH = "
|
|
2
|
+
export const VERSION = "2.9.11";
|
|
3
|
+
export const GIT_HASH = "4384d75";
|
package/build/git/core.js
CHANGED
|
@@ -4,6 +4,28 @@ import { normalizeFilesystemCoordinate } from "../fs/path-coordinate.js";
|
|
|
4
4
|
import { getConfig } from "../config/env.js";
|
|
5
5
|
import { appendDebugBlock } from "../telemetry/debug-log.js";
|
|
6
6
|
const GIT_ERROR_COMPACT_MAX_CHARS = 4000;
|
|
7
|
+
export const GIT_ABORT_FORCE_KILL_MS = 250;
|
|
8
|
+
/** Terminate one non-detached Git child and wait until Node drains its stdio close. */
|
|
9
|
+
export async function terminateGitChild(child, closed, graceMs = GIT_ABORT_FORCE_KILL_MS) {
|
|
10
|
+
if (child.exitCode === null && child.signalCode === null)
|
|
11
|
+
child.kill("SIGTERM");
|
|
12
|
+
let grace;
|
|
13
|
+
const exited = await Promise.race([
|
|
14
|
+
closed.then(() => true),
|
|
15
|
+
new Promise((resolve) => {
|
|
16
|
+
grace = setTimeout(() => resolve(false), graceMs);
|
|
17
|
+
grace.unref();
|
|
18
|
+
}),
|
|
19
|
+
]);
|
|
20
|
+
if (grace)
|
|
21
|
+
clearTimeout(grace);
|
|
22
|
+
// Only the close event proves process exit and stdio drain. `killed` and
|
|
23
|
+
// `signalCode` merely report signal delivery and can be set while a handler
|
|
24
|
+
// keeps the child alive.
|
|
25
|
+
if (!exited)
|
|
26
|
+
child.kill("SIGKILL");
|
|
27
|
+
await closed;
|
|
28
|
+
}
|
|
7
29
|
export const MISSING_GIT_CONFIG_PATTERNS = ["No such section or key"];
|
|
8
30
|
export const NOT_GIT_REPOSITORY_PATTERNS = ["not a git repository"];
|
|
9
31
|
export const MISSING_HEAD_PATTERNS = [
|
|
@@ -12,6 +34,10 @@ export const MISSING_HEAD_PATTERNS = [
|
|
|
12
34
|
"unknown revision or path not in the working tree",
|
|
13
35
|
"bad revision 'HEAD'",
|
|
14
36
|
];
|
|
37
|
+
/** Accept only a complete object id from either supported Git object format. */
|
|
38
|
+
export function isFullGitObjectId(value) {
|
|
39
|
+
return /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/i.test(value);
|
|
40
|
+
}
|
|
15
41
|
/** Git is an optional adapter for directory-local product surfaces. */
|
|
16
42
|
export function isGitExecutableUnavailable(error) {
|
|
17
43
|
const source = (error ?? {});
|
|
@@ -94,11 +120,18 @@ export function createGit(cwd) {
|
|
|
94
120
|
const git = simpleGit(normalizeFilesystemCoordinate(cwd), {
|
|
95
121
|
timeout: { block: timeoutMs, stdErr: true, stdOut: true },
|
|
96
122
|
});
|
|
97
|
-
git
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
git.env(
|
|
123
|
+
// simple-git replaces its child environment when configured through env().
|
|
124
|
+
// Preserve trace controls explicitly without admitting unrelated ambient Git
|
|
125
|
+
// behavior such as a pager into command execution.
|
|
126
|
+
const traceEnvironment = Object.fromEntries(Object.entries(process.env).filter(([key]) => key.startsWith("GIT_TRACE2")));
|
|
127
|
+
git.env({
|
|
128
|
+
...traceEnvironment,
|
|
129
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
130
|
+
GCM_INTERACTIVE: "Never",
|
|
131
|
+
GIT_MERGE_AUTOEDIT: "no",
|
|
132
|
+
LC_ALL: "C",
|
|
133
|
+
PATH: process.env.PATH ?? "",
|
|
134
|
+
});
|
|
102
135
|
return git;
|
|
103
136
|
}
|
|
104
137
|
/**
|
|
@@ -107,7 +140,8 @@ export function createGit(cwd) {
|
|
|
107
140
|
* soft status codes or stdin (simple-git drops empty-stderr non-zero exits).
|
|
108
141
|
* Long-lived multi-request stdin/stdout sessions use `openGitStreamingBatch`.
|
|
109
142
|
*/
|
|
110
|
-
export async function
|
|
143
|
+
export async function runGitProcessBytes(cwd, args, options) {
|
|
144
|
+
options?.signal?.throwIfAborted();
|
|
111
145
|
const timeoutMs = resolveGitTimeoutMs();
|
|
112
146
|
const env = gitProcessEnv(options?.env);
|
|
113
147
|
return await new Promise((resolve) => {
|
|
@@ -121,19 +155,35 @@ export async function runGitProcess(cwd, args, options) {
|
|
|
121
155
|
const stderrChunks = [];
|
|
122
156
|
let settled = false;
|
|
123
157
|
let timedOut = false;
|
|
158
|
+
let aborting = false;
|
|
159
|
+
let terminationPromise;
|
|
160
|
+
const closed = new Promise((resolveClosed) => child.once("close", () => resolveClosed()));
|
|
124
161
|
const finish = (result) => {
|
|
125
162
|
if (settled)
|
|
126
163
|
return;
|
|
127
164
|
settled = true;
|
|
128
165
|
clearTimeout(timeout);
|
|
166
|
+
options?.signal?.removeEventListener("abort", abort);
|
|
129
167
|
resolve(result);
|
|
130
168
|
};
|
|
169
|
+
const abort = () => {
|
|
170
|
+
if (aborting)
|
|
171
|
+
return;
|
|
172
|
+
aborting = true;
|
|
173
|
+
terminationPromise = terminateGitChild(child, closed);
|
|
174
|
+
};
|
|
175
|
+
options?.signal?.addEventListener("abort", abort, { once: true });
|
|
176
|
+
if (options?.signal?.aborted)
|
|
177
|
+
abort();
|
|
131
178
|
const timeout = setTimeout(() => {
|
|
132
179
|
timedOut = true;
|
|
133
180
|
child.kill("SIGKILL");
|
|
134
181
|
}, timeoutMs);
|
|
135
182
|
if (options?.input !== undefined) {
|
|
136
|
-
|
|
183
|
+
if (typeof options.input === "string")
|
|
184
|
+
child.stdin.end(options.input, "utf8");
|
|
185
|
+
else
|
|
186
|
+
child.stdin.end(options.input);
|
|
137
187
|
}
|
|
138
188
|
else {
|
|
139
189
|
child.stdin.end();
|
|
@@ -142,15 +192,17 @@ export async function runGitProcess(cwd, args, options) {
|
|
|
142
192
|
child.stderr.on("data", (chunk) => stderrChunks.push(chunk));
|
|
143
193
|
child.on("error", (error) => {
|
|
144
194
|
finish({
|
|
145
|
-
stdout: Buffer.concat(stdoutChunks)
|
|
146
|
-
stderr: Buffer.concat(stderrChunks)
|
|
195
|
+
stdout: Buffer.concat(stdoutChunks),
|
|
196
|
+
stderr: Buffer.concat(stderrChunks),
|
|
147
197
|
status: null,
|
|
148
198
|
error,
|
|
149
199
|
});
|
|
150
200
|
});
|
|
151
|
-
child.on("close", (code, signal) => {
|
|
152
|
-
|
|
153
|
-
|
|
201
|
+
child.on("close", async (code, signal) => {
|
|
202
|
+
if (terminationPromise)
|
|
203
|
+
await terminationPromise;
|
|
204
|
+
const stdout = Buffer.concat(stdoutChunks);
|
|
205
|
+
const stderr = Buffer.concat(stderrChunks);
|
|
154
206
|
if (timedOut) {
|
|
155
207
|
finish({
|
|
156
208
|
stdout,
|
|
@@ -165,7 +217,9 @@ export async function runGitProcess(cwd, args, options) {
|
|
|
165
217
|
stdout,
|
|
166
218
|
stderr,
|
|
167
219
|
status: null,
|
|
168
|
-
error:
|
|
220
|
+
error: options?.signal?.aborted
|
|
221
|
+
? options.signal.reason instanceof Error ? options.signal.reason : new DOMException("The operation was aborted", "AbortError")
|
|
222
|
+
: new Error(`git exited from signal ${signal}`),
|
|
169
223
|
});
|
|
170
224
|
return;
|
|
171
225
|
}
|
|
@@ -173,3 +227,12 @@ export async function runGitProcess(cwd, args, options) {
|
|
|
173
227
|
});
|
|
174
228
|
});
|
|
175
229
|
}
|
|
230
|
+
export async function runGitProcess(cwd, args, options) {
|
|
231
|
+
const result = await runGitProcessBytes(cwd, args, options);
|
|
232
|
+
return {
|
|
233
|
+
stdout: result.stdout.toString("utf8"),
|
|
234
|
+
stderr: result.stderr.toString("utf8"),
|
|
235
|
+
status: result.status,
|
|
236
|
+
...(result.error ? { error: result.error } : {}),
|
|
237
|
+
};
|
|
238
|
+
}
|