@astrosheep/keiyaku 2.9.11 → 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 +79 -85
- package/build/.tsbuildinfo +1 -1
- package/build/agents/call-terms.js +1 -0
- package/build/agents/providers/claude-agent-sdk/adapter.js +17 -4
- 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/bind/meta.js +4 -4
- 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 +14 -10
- 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/handler.js +4 -5
- 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 +2 -2
- 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 +4 -0
- package/build/cli/commands/task/compose/handler.js +18 -0
- package/build/cli/commands/task/compose/meta.js +10 -0
- 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 +48 -12
- package/build/cli/help.js +29 -30
- package/build/cli/index.js +14 -8
- package/build/cli/parse-flags.js +60 -21
- package/build/cli/parse-metadata.js +10 -4
- package/build/cli/parse-selectors.js +22 -4
- package/build/cli/parse.js +43 -30
- package/build/cli/projection-address.js +7 -7
- 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 +33 -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/addressing.js +3 -3
- 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 +46 -33
- 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 +43 -6
- package/build/core/contract.js +2 -1
- package/build/core/derived-replay.js +168 -35
- package/build/core/draft.js +60 -27
- 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 +19 -14
- 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 +132 -76
- 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 +24 -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 +8 -8
- package/build/core/projection/projection-kill.js +20 -10
- 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 +48 -29
- package/build/core/projection/projection-wake.js +28 -88
- 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/claim-evidence.js +1 -1
- package/build/core/repository-ledger/codec.js +11 -5
- package/build/core/repository-ledger/current-state-store.js +464 -349
- 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 +57 -9
- package/build/core/repository-ledger/write-transaction.js +24 -17
- 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/run-control/sqlite-process-lifetime-lock.js +174 -0
- 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 +328 -0
- package/build/core/task/document.js +102 -39
- package/build/core/task/index.js +4 -3
- package/build/core/task/settlement-git.js +70 -30
- 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-git-store.js +1 -0
- package/build/core/task/task-store-repository.js +2 -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 +15 -2
- 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 +2 -2
- package/skills/keiyaku-akuma/SKILL.md +27 -21
- package/skills/keiyaku-task/SKILL.md +61 -5
- package/skills/keiyaku-workflow/SKILL.md +81 -26
- 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 -382
- 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
|
@@ -5,6 +5,12 @@ import { runGitProcess } from "../../git/core.js";
|
|
|
5
5
|
import { openGitStreamingBatch } from "../../git/streaming-batch.js";
|
|
6
6
|
import { parseTaskDocument } from "./task.js";
|
|
7
7
|
import { parseTaskCoordinate } from "./coordinate.js";
|
|
8
|
+
function observePublicationEffect(observer, effect) {
|
|
9
|
+
try {
|
|
10
|
+
observer?.(effect);
|
|
11
|
+
}
|
|
12
|
+
catch { /* observation cannot participate in the Git transaction */ }
|
|
13
|
+
}
|
|
8
14
|
async function gitResult(cwd, args, input, env, signal) {
|
|
9
15
|
return await runGitProcess(cwd, args, { input, env, signal });
|
|
10
16
|
}
|
|
@@ -78,9 +84,11 @@ export async function createTaskMutationCommit(input) {
|
|
|
78
84
|
], undefined, env, input.signal);
|
|
79
85
|
}
|
|
80
86
|
const tree = (await git(input.cwd, ["write-tree"], undefined, env, input.signal)).trim();
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
87
|
+
if (!input.requireCommit) {
|
|
88
|
+
const parentTree = (await git(input.cwd, ["show", "-s", "--format=%T", input.parent], undefined, undefined, input.signal)).trim();
|
|
89
|
+
if (tree === parentTree)
|
|
90
|
+
return input.parent;
|
|
91
|
+
}
|
|
84
92
|
return (await git(input.cwd, ["commit-tree", tree, "-p", input.parent, "-m", input.subject], undefined, undefined, input.signal)).trim();
|
|
85
93
|
}
|
|
86
94
|
finally {
|
|
@@ -88,15 +96,37 @@ export async function createTaskMutationCommit(input) {
|
|
|
88
96
|
}
|
|
89
97
|
}
|
|
90
98
|
/** Stage only the mutated task paths into the real index; leave unrelated entries alone. */
|
|
91
|
-
|
|
99
|
+
async function reconcileTaskMutationIndexPath(cwd, write, signal) {
|
|
100
|
+
const oid = (await git(cwd, ["hash-object", "-w", "--stdin"], write.text, undefined, signal)).trim();
|
|
101
|
+
await git(cwd, [
|
|
102
|
+
"update-index",
|
|
103
|
+
"--add",
|
|
104
|
+
"--cacheinfo",
|
|
105
|
+
`100644,${oid},${taskMutationPath(write)}`,
|
|
106
|
+
], undefined, undefined, signal);
|
|
107
|
+
}
|
|
108
|
+
export async function reconcileTaskMutationIndex(cwd, writes, signal, reconcilePath = async (input) => {
|
|
109
|
+
await reconcileTaskMutationIndexPath(input.cwd, input.write, input.signal);
|
|
110
|
+
}) {
|
|
92
111
|
for (const write of writes) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
112
|
+
await reconcilePath({ cwd, write, signal });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async function snapshotTaskMutationIndex(cwd, writes, signal) {
|
|
116
|
+
const snapshot = [];
|
|
117
|
+
for (const write of writes) {
|
|
118
|
+
const taskPath = taskMutationPath(write);
|
|
119
|
+
const output = await git(cwd, ["ls-files", "--stage", "-z", "--", taskPath], undefined, undefined, signal);
|
|
120
|
+
snapshot.push({ path: taskPath, entries: output.split("\0").filter(Boolean) });
|
|
121
|
+
}
|
|
122
|
+
return snapshot;
|
|
123
|
+
}
|
|
124
|
+
async function restoreTaskMutationIndex(cwd, snapshot, signal) {
|
|
125
|
+
for (const item of snapshot) {
|
|
126
|
+
await git(cwd, ["update-index", "--force-remove", "--", item.path], undefined, undefined, signal);
|
|
127
|
+
if (item.entries.length > 0) {
|
|
128
|
+
await git(cwd, ["update-index", "-z", "--index-info"], `${item.entries.join("\0")}\0`, undefined, signal);
|
|
129
|
+
}
|
|
100
130
|
}
|
|
101
131
|
}
|
|
102
132
|
/**
|
|
@@ -114,20 +144,33 @@ export async function publishTaskMutationCommit(input, deps = {}) {
|
|
|
114
144
|
parent: branch.head,
|
|
115
145
|
writes: input.writes,
|
|
116
146
|
subject: input.subject,
|
|
147
|
+
requireCommit: input.requireCommit,
|
|
117
148
|
signal: input.signal,
|
|
118
149
|
});
|
|
119
150
|
if (commit === branch.head) {
|
|
120
|
-
|
|
151
|
+
const indexBefore = await snapshotTaskMutationIndex(input.cwd, input.writes, input.signal);
|
|
121
152
|
try {
|
|
122
|
-
await reconcileTaskMutationIndex(input.cwd, input.writes, input.signal);
|
|
123
|
-
input.observeEffect
|
|
153
|
+
await reconcileTaskMutationIndex(input.cwd, input.writes, input.signal, deps.reconcileIndexPath);
|
|
154
|
+
observePublicationEffect(input.observeEffect, { effect: "task-reconciliation", status: "succeeded", commit, detail: "current" });
|
|
155
|
+
observePublicationEffect(input.observeEffect, { effect: "task-index-reconciliation", status: "succeeded", commit });
|
|
156
|
+
return commit;
|
|
124
157
|
}
|
|
125
158
|
catch (error) {
|
|
126
|
-
|
|
159
|
+
try {
|
|
160
|
+
await restoreTaskMutationIndex(input.cwd, indexBefore, input.signal);
|
|
161
|
+
}
|
|
162
|
+
catch (restoreError) {
|
|
163
|
+
observePublicationEffect(input.observeEffect, { effect: "task-index-reconciliation", status: "failed", commit, error: restoreError });
|
|
164
|
+
throw new FlowError("INTERNAL_STATE", "cannot restore task index after reconciliation failure", { cause: restoreError });
|
|
165
|
+
}
|
|
166
|
+
observePublicationEffect(input.observeEffect, { effect: "task-index-reconciliation", status: "failed", commit, error });
|
|
167
|
+
throw error;
|
|
127
168
|
}
|
|
128
|
-
return commit;
|
|
129
169
|
}
|
|
170
|
+
const indexBefore = await snapshotTaskMutationIndex(input.cwd, input.writes, input.signal);
|
|
130
171
|
try {
|
|
172
|
+
await reconcileTaskMutationIndex(input.cwd, input.writes, input.signal, deps.reconcileIndexPath);
|
|
173
|
+
observePublicationEffect(input.observeEffect, { effect: "task-index-reconciliation", status: "succeeded", commit });
|
|
131
174
|
await updateBranch({
|
|
132
175
|
cwd: input.cwd,
|
|
133
176
|
ref: branch.ref,
|
|
@@ -135,22 +178,22 @@ export async function publishTaskMutationCommit(input, deps = {}) {
|
|
|
135
178
|
expected: branch.head,
|
|
136
179
|
signal: input.signal,
|
|
137
180
|
}, deps);
|
|
138
|
-
input.observeEffect
|
|
139
|
-
try {
|
|
140
|
-
await reconcileTaskMutationIndex(input.cwd, input.writes, input.signal);
|
|
141
|
-
input.observeEffect?.({ effect: "task-index-reconciliation", status: "succeeded", commit });
|
|
142
|
-
}
|
|
143
|
-
catch (error) {
|
|
144
|
-
// Branch fact remains authoritative; index may be repaired by a later mutation.
|
|
145
|
-
input.observeEffect?.({ effect: "task-index-reconciliation", status: "failed", commit, error });
|
|
146
|
-
}
|
|
181
|
+
observePublicationEffect(input.observeEffect, { effect: "task-reconciliation", status: "succeeded", commit, detail: "published" });
|
|
147
182
|
return commit;
|
|
148
183
|
}
|
|
149
184
|
catch (error) {
|
|
150
185
|
lastError = error;
|
|
186
|
+
try {
|
|
187
|
+
await restoreTaskMutationIndex(input.cwd, indexBefore, input.signal);
|
|
188
|
+
}
|
|
189
|
+
catch (restoreError) {
|
|
190
|
+
observePublicationEffect(input.observeEffect, { effect: "task-index-reconciliation", status: "failed", commit, error: restoreError });
|
|
191
|
+
throw new FlowError("INTERNAL_STATE", "cannot restore task index after publication failure", { cause: restoreError });
|
|
192
|
+
}
|
|
193
|
+
observePublicationEffect(input.observeEffect, { effect: "task-index-reconciliation", status: "failed", commit, error });
|
|
151
194
|
}
|
|
152
195
|
}
|
|
153
|
-
input.observeEffect
|
|
196
|
+
observePublicationEffect(input.observeEffect, { effect: "task-reconciliation", status: "failed", error: lastError });
|
|
154
197
|
throw lastError;
|
|
155
198
|
}
|
|
156
199
|
async function taskTreeEntries(cwd, commit) {
|
|
@@ -235,10 +278,7 @@ async function readTaskAdmissionEvidence(input, readDocument = taskDocumentAtCom
|
|
|
235
278
|
const needs = [];
|
|
236
279
|
for (const need of document.needs) {
|
|
237
280
|
const prerequisite = await read(need);
|
|
238
|
-
|
|
239
|
-
throw new FlowError("INTERNAL_STATE", `bind base ${input.base} task ${taskId} has unsatisfied need ${need}`);
|
|
240
|
-
}
|
|
241
|
-
needs.push({ taskId: need, state: prerequisite.state });
|
|
281
|
+
needs.push({ taskId: need, state: prerequisite.state, ...(prerequisite.contractId === undefined ? {} : { contractId: prerequisite.contractId }) });
|
|
242
282
|
}
|
|
243
283
|
selected.push({ taskId, needs });
|
|
244
284
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { FlowError } from "../../flow-error.js";
|
|
2
2
|
import { resolveTaskReadId } from "./board.js";
|
|
3
|
+
import { openTaskStore } from "./task-git-store.js";
|
|
4
|
+
import { taskRepositoryFromStore } from "./task-store-repository.js";
|
|
5
|
+
import { readTaskBoard } from "./commands.js";
|
|
3
6
|
import { advancedTaskUpdatedAt } from "./model.js";
|
|
4
7
|
export function requireHealthySettlementBoard(board) {
|
|
5
8
|
if (board.diseases.length > 0) {
|
|
@@ -20,6 +23,7 @@ function settlementDocument(task, contractId, updatedAt) {
|
|
|
20
23
|
from: [...task.from],
|
|
21
24
|
supersedes: [...task.supersedes],
|
|
22
25
|
relatesTo: [...task.relatesTo],
|
|
26
|
+
notes: task.notes.map((note) => ({ ...note })),
|
|
23
27
|
createdAt: task.createdAt,
|
|
24
28
|
updatedAt: advancedTaskUpdatedAt(task, updatedAt),
|
|
25
29
|
creator: task.creator,
|
|
@@ -31,6 +35,12 @@ function settlementDocument(task, contractId, updatedAt) {
|
|
|
31
35
|
export function contractPointerDocument(task, contractId, updatedAt) {
|
|
32
36
|
return settlementDocument(task, contractId, updatedAt);
|
|
33
37
|
}
|
|
38
|
+
export function boundTaskDocument(task, contractId, updatedAt) {
|
|
39
|
+
const document = settlementDocument(task, contractId, updatedAt);
|
|
40
|
+
document.state = "open";
|
|
41
|
+
delete document.startedAt;
|
|
42
|
+
return document;
|
|
43
|
+
}
|
|
34
44
|
export function reopenedTaskDocument(task, updatedAt) {
|
|
35
45
|
const document = settlementDocument(task, undefined, updatedAt);
|
|
36
46
|
document.state = "open";
|
|
@@ -38,25 +48,28 @@ export function reopenedTaskDocument(task, updatedAt) {
|
|
|
38
48
|
return document;
|
|
39
49
|
}
|
|
40
50
|
function validateReadyForBind(board, task) {
|
|
41
|
-
if (task.state
|
|
42
|
-
throw new FlowError("INVALID_TARGET", `task ${task.coordinate} is
|
|
43
|
-
}
|
|
44
|
-
if (task.state !== "open") {
|
|
45
|
-
throw new FlowError("INVALID_TARGET", `task ${task.coordinate} is ${task.state}, not ready for bind`);
|
|
46
|
-
}
|
|
47
|
-
const unsatisfied = task.needs
|
|
48
|
-
.filter((need) => {
|
|
49
|
-
const dependency = board.byId.get(need);
|
|
50
|
-
return dependency.state === "open" || dependency.state === "in_progress" || dependency.state === "on_hold";
|
|
51
|
-
})
|
|
52
|
-
.sort();
|
|
53
|
-
if (unsatisfied.length > 0) {
|
|
54
|
-
throw new FlowError("INVALID_TARGET", `task ${task.coordinate} is blocked by: ${unsatisfied.join(", ")}`);
|
|
51
|
+
if (task.state !== "open" && task.state !== "in_progress") {
|
|
52
|
+
throw new FlowError("INVALID_TARGET", `task ${task.coordinate} is ${task.state}, not open or in_progress for bind`);
|
|
55
53
|
}
|
|
56
54
|
if (task.contractId !== undefined) {
|
|
57
55
|
throw new FlowError("INVALID_TARGET", `task ${task.coordinate} is already bound to contract ${task.contractId}`);
|
|
58
56
|
}
|
|
59
57
|
}
|
|
58
|
+
/** Read the sole live task prerequisite gate for a task-selected contract. */
|
|
59
|
+
export async function readRemainingTaskNeedsForSettlement(cwd, evidence) {
|
|
60
|
+
const needIds = [...new Set(evidence?.selected.flatMap((task) => task.needs
|
|
61
|
+
.filter((need) => need.contractId === undefined)
|
|
62
|
+
.map((need) => need.taskId)) ?? [])].sort();
|
|
63
|
+
if (needIds.length === 0)
|
|
64
|
+
return [];
|
|
65
|
+
const board = requireHealthySettlementBoard(await readTaskBoard(taskRepositoryFromStore(await openTaskStore(cwd))));
|
|
66
|
+
return needIds.map((id) => {
|
|
67
|
+
const task = board.byId.get(id);
|
|
68
|
+
if (!task)
|
|
69
|
+
throw new FlowError("INTERNAL_STATE", `task prerequisite ${id} disappeared from the authoritative board`);
|
|
70
|
+
return { id, satisfied: task.state === "done" || task.state === "drop", state: task.state };
|
|
71
|
+
});
|
|
72
|
+
}
|
|
60
73
|
export function selectReadyTasksForBind(board, taskIds) {
|
|
61
74
|
requireHealthySettlementBoard(board);
|
|
62
75
|
const rootIds = taskIds.map((taskId) => resolveTaskReadId(board, taskId));
|
|
@@ -24,6 +24,7 @@ function taskDocument(parsed) {
|
|
|
24
24
|
from: [...parsed.from],
|
|
25
25
|
supersedes: [...parsed.supersedes],
|
|
26
26
|
relatesTo: [...parsed.relatesTo],
|
|
27
|
+
notes: parsed.notes.map((note) => ({ ...note })),
|
|
27
28
|
createdAt: parsed.createdAt,
|
|
28
29
|
...(parsed.updatedAt === undefined ? {} : { updatedAt: parsed.updatedAt }),
|
|
29
30
|
creator: parsed.creator,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { FlowError } from "../../flow-error.js";
|
|
2
|
+
import { readCommitCoordinate } from "../../git/commits.js";
|
|
2
3
|
import { runGitProcess } from "../../git/core.js";
|
|
3
4
|
import { assertGitObservation, requireGitRepository } from "../../git/branches.js";
|
|
4
5
|
import { inspectCurrentBranch, createTaskBoardCommit, publishForwardTaskBoardCommit, reconcileTaskPathsetIndex, refHead, taskSourcesAtCommit, taskWritesFromSources, } from "./settlement-git.js";
|
|
@@ -21,6 +22,7 @@ function linkedTaskDocument(task, contractId, at) {
|
|
|
21
22
|
from: [...task.from],
|
|
22
23
|
supersedes: [...task.supersedes],
|
|
23
24
|
relatesTo: [...task.relatesTo],
|
|
25
|
+
notes: task.notes.map((note) => ({ ...note })),
|
|
24
26
|
createdAt: task.createdAt,
|
|
25
27
|
updatedAt: advancedTaskUpdatedAt(task, at),
|
|
26
28
|
creator: task.creator,
|
|
@@ -32,11 +34,10 @@ function selectedEvidence(board, selected) {
|
|
|
32
34
|
return selected.map((task) => ({
|
|
33
35
|
taskId: task.coordinate,
|
|
34
36
|
needs: task.needs.map((taskId) => {
|
|
35
|
-
const
|
|
36
|
-
if (
|
|
37
|
-
throw new FlowError("INTERNAL_STATE", `
|
|
38
|
-
}
|
|
39
|
-
return { taskId, state };
|
|
37
|
+
const need = board.byId.get(taskId);
|
|
38
|
+
if (!need)
|
|
39
|
+
throw new FlowError("INTERNAL_STATE", `selected task ${task.coordinate} has missing need ${taskId}`);
|
|
40
|
+
return { taskId, state: need.state, ...(need.contractId === undefined ? {} : { contractId: need.contractId }) };
|
|
40
41
|
}),
|
|
41
42
|
}));
|
|
42
43
|
}
|
|
@@ -100,6 +101,52 @@ export async function withPreparedTaskBind(input, deps = {}) {
|
|
|
100
101
|
return { preparation, result };
|
|
101
102
|
}, { signal: input.signal });
|
|
102
103
|
}
|
|
104
|
+
/** Task-lock admission from bind's already-observed repository and board coordinates. */
|
|
105
|
+
export async function withPreparedTaskBindSnapshot(input, deps = {}) {
|
|
106
|
+
return await withLockHeldTaskBoard(input.location.root, async (transaction) => {
|
|
107
|
+
const prepared = await prepareLockedTaskBindFromSnapshot(transaction, input, deps);
|
|
108
|
+
return { preparation: prepared, result: await input.transact(prepared, transaction) };
|
|
109
|
+
}, { signal: input.signal }, input.location);
|
|
110
|
+
}
|
|
111
|
+
async function prepareLockedTaskBindFromSnapshot(transaction, input, deps) {
|
|
112
|
+
if (transaction.location.mode !== "git")
|
|
113
|
+
throw new FlowError("NOT_GIT_REPO", "task bind requires Git task storage");
|
|
114
|
+
const observedBoard = await input.observeBoard();
|
|
115
|
+
const board = requireHealthySettlementBoard(taskBoardFromSourceBoard(buildTaskSourceBoard(transaction.sources)));
|
|
116
|
+
const selected = selectReadyTasksForBind(board, input.taskIds);
|
|
117
|
+
if (selected.length === 0)
|
|
118
|
+
throw new FlowError("INVALID_TARGET", "task bind requires at least one selected task");
|
|
119
|
+
const attempt = await input.prepare(selected);
|
|
120
|
+
const evidence = selectedEvidence(board, selected);
|
|
121
|
+
const linked = prepareTaskDocumentReplacements(transaction.sources, selected.map((task) => linkedTaskDocument(task, attempt.contractId, attempt.at)));
|
|
122
|
+
const linkedIds = new Set(selected.map((task) => task.coordinate));
|
|
123
|
+
const linkedSources = linked.sources.filter((source) => linkedIds.has(taskCoordinateFromSourcePath(source.path).coordinate));
|
|
124
|
+
const originalSources = transaction.sources.filter((source) => linkedIds.has(taskCoordinateFromSourcePath(source.path).coordinate));
|
|
125
|
+
const newHead = await (deps.createTaskBoardCommit ?? createTaskBoardCommit)({
|
|
126
|
+
cwd: transaction.location.root,
|
|
127
|
+
gitDir: transaction.location.gitDir,
|
|
128
|
+
root: transaction.location.root,
|
|
129
|
+
parent: observedBoard.head,
|
|
130
|
+
sources: linkedSources,
|
|
131
|
+
subject: selected.length === 1
|
|
132
|
+
? `Bind task ${selected[0].coordinate} to contract ${attempt.contractId}`
|
|
133
|
+
: `Bind ${selected.length} tasks to contract ${attempt.contractId}`,
|
|
134
|
+
});
|
|
135
|
+
return {
|
|
136
|
+
boardRef: observedBoard.ref,
|
|
137
|
+
expectedHead: observedBoard.head,
|
|
138
|
+
newHead,
|
|
139
|
+
entryBase: newHead,
|
|
140
|
+
taskIds: selected.map((task) => task.coordinate),
|
|
141
|
+
selected: evidence,
|
|
142
|
+
taskBoard: { ref: observedBoard.ref, expectedHead: observedBoard.head, head: newHead },
|
|
143
|
+
replay: {
|
|
144
|
+
paths: selected.map((task) => `.keiyaku/tasks/${task.coordinate}.md`).sort(),
|
|
145
|
+
linkedSources,
|
|
146
|
+
originalSources,
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
}
|
|
103
150
|
/** Complete the prepared task projection while its authority transaction still owns the task lock. */
|
|
104
151
|
export async function replayPreparedTaskBindLocked(transaction, prepared) {
|
|
105
152
|
if (transaction.location.mode !== "git")
|
|
@@ -149,11 +196,14 @@ export async function replayCommittedTaskBindForfeit(cwd, commit, boardRef, task
|
|
|
149
196
|
return await withLockHeldTaskBoard(cwd, async (transaction) => {
|
|
150
197
|
if (transaction.location.mode !== "git")
|
|
151
198
|
throw new FlowError("NOT_GIT_REPO", "task forfeit replay requires Git task storage");
|
|
152
|
-
const
|
|
153
|
-
if (
|
|
154
|
-
throw new FlowError("INTERNAL_STATE", `committed task bind base ${commit} has no readable parent`, { cause:
|
|
199
|
+
const coordinate = await readCommitCoordinate(transaction.location.root, commit);
|
|
200
|
+
if (coordinate.status !== "ok") {
|
|
201
|
+
throw new FlowError("INTERNAL_STATE", `committed task bind base ${commit} has no readable parent`, { cause: coordinate.status === "failed" ? coordinate.error : undefined });
|
|
202
|
+
}
|
|
203
|
+
const parent = coordinate.coordinate.parents[0];
|
|
204
|
+
if (!parent) {
|
|
205
|
+
throw new FlowError("INTERNAL_STATE", `committed task bind base ${commit} has no readable parent`);
|
|
155
206
|
}
|
|
156
|
-
const parent = parentResult.stdout.trim();
|
|
157
207
|
const paths = taskIds.map((taskId) => `.keiyaku/tasks/${taskId}.md`).sort();
|
|
158
208
|
const originalSources = await taskSourcesAtCommit(transaction.location.root, transaction.location.root, parent, paths);
|
|
159
209
|
if (!originalSources)
|
|
@@ -5,7 +5,7 @@ import { prepareTaskDocumentReplacements, taskBoardFromSourceBoard, taskReposito
|
|
|
5
5
|
import { buildTaskSourceBoard } from "./source-board.js";
|
|
6
6
|
import { formatTaskCoordinate } from "./coordinate.js";
|
|
7
7
|
import { createTaskBoardCommit, publishTaskMutationCommit, inspectCurrentBranch, publishForwardTaskBoardCommit, reconcileTaskPathsetIndex, refHead, taskContractAtCommit, taskSourcesAtCommit, taskWritesFromSources, trackedTaskPaths, updateBranch, } from "./settlement-git.js";
|
|
8
|
-
import { contractPointerDocument, doneTaskReplacements, reopenedTaskDocument, requireHealthySettlementBoard, requireSettledTask, selectReadyTasksForBind, tasksForContract, } from "./settlement-policy.js";
|
|
8
|
+
import { boundTaskDocument, contractPointerDocument, doneTaskReplacements, reopenedTaskDocument, requireHealthySettlementBoard, requireSettledTask, selectReadyTasksForBind, tasksForContract, } from "./settlement-policy.js";
|
|
9
9
|
import { stableRepoRoot } from "../worktree-path.js";
|
|
10
10
|
/**
|
|
11
11
|
* Read the same healthy task board and run the same ready-task selector as bind
|
|
@@ -147,7 +147,7 @@ export async function settleTaskBoardForBind(input, deps = {}) {
|
|
|
147
147
|
const prepared = taskBound ? await input.prepare(selectedTasks) : input;
|
|
148
148
|
const contractId = prepared.contractId;
|
|
149
149
|
if (taskBound && selectedTasks.length > 0) {
|
|
150
|
-
await replaceTaskDocuments(repository, selectedTasks.map((task) =>
|
|
150
|
+
await replaceTaskDocuments(repository, selectedTasks.map((task) => boundTaskDocument(task, contractId, updatedAt)));
|
|
151
151
|
}
|
|
152
152
|
const taskIds = taskBound ? selectedTasks.map((t) => t.coordinate) : undefined;
|
|
153
153
|
const selectedPaths = ownedTaskPaths(await repository.read(), taskIds);
|
|
@@ -15,6 +15,7 @@ export function taskBoardFromSourceBoard(sourceBoard) {
|
|
|
15
15
|
from: [...task.from],
|
|
16
16
|
supersedes: [...task.supersedes],
|
|
17
17
|
relatesTo: [...task.relatesTo],
|
|
18
|
+
notes: task.notes.map((note) => ({ ...note })),
|
|
18
19
|
createdAt: task.createdAt,
|
|
19
20
|
...(task.updatedAt === undefined ? {} : { updatedAt: task.updatedAt }),
|
|
20
21
|
creator: task.creator,
|
|
@@ -75,6 +76,7 @@ function mutationPlan(sources, mutation) {
|
|
|
75
76
|
writes: prepared.writes,
|
|
76
77
|
value: mutation.select(prepared.board),
|
|
77
78
|
...(mutation.subject === undefined ? {} : { subject: mutation.subject }),
|
|
79
|
+
requireCommit: mutation.requireCommit ?? true,
|
|
78
80
|
};
|
|
79
81
|
}
|
|
80
82
|
export function taskRepositoryFromStore(store) {
|
package/build/core/task/task.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { deriveTaskDisplay, parseTaskFilter, parseTaskSort, selectTaskList } from "./query.js";
|
|
2
2
|
export { advancedTaskUpdatedAt, taskUpdatedAt } from "./model.js";
|
|
3
|
-
export { parseTaskDocument, serializeTaskDocument, } from "./document.js";
|
|
3
|
+
export { parseTaskCreationDocument, parseTaskDocument, serializeTaskDocument, } from "./document.js";
|
|
4
4
|
export { resolveTaskReadId, } from "./board.js";
|
|
5
5
|
export { buildTaskBoard } from "./source-board.js";
|
|
6
6
|
export { buildTaskTree } from "./tree.js";
|
|
7
|
-
export { readTaskBoard, createTask, updateTask, updateTaskWithReceipt, startTask, stopTask, holdTask, resumeTask, doneTask, dropTask, selectSoleInProgress, rollbackOrphanTaskPointers, } from "./commands.js";
|
|
7
|
+
export { readTaskBoard, createTask, updateTask, updateTaskWithReceipt, startTask, stopTask, holdTask, resumeTask, doneTask, dropTask, dropTaskWithNote, appendTaskNote, selectSoleInProgress, rollbackOrphanTaskPointers, } from "./commands.js";
|
|
@@ -410,7 +410,7 @@ export async function locateResponseArtifact(input) {
|
|
|
410
410
|
return carry(error);
|
|
411
411
|
}
|
|
412
412
|
if (!isResponseArtifactId(identity)) {
|
|
413
|
-
return carry(new FlowError("INVALID_RESPONSE_PATH", `${identity} is not a response artifact handle;
|
|
413
|
+
return carry(new FlowError("INVALID_RESPONSE_PATH", `${identity} is not a response artifact handle; fork expects an artifact id`));
|
|
414
414
|
}
|
|
415
415
|
let physicalLocalRoot;
|
|
416
416
|
try {
|
|
@@ -463,11 +463,11 @@ export async function locateResponseArtifact(input) {
|
|
|
463
463
|
}
|
|
464
464
|
export function requireResumableArtifact(artifact) {
|
|
465
465
|
if (!artifact.session)
|
|
466
|
-
throw artifactError(artifact.artifactId, RESPONSE_HISTORY_SESSION_KEY, "is missing; this conversation cannot be
|
|
466
|
+
throw artifactError(artifact.artifactId, RESPONSE_HISTORY_SESSION_KEY, "is missing; this conversation cannot be forked");
|
|
467
467
|
if (!artifact.subagentName)
|
|
468
|
-
throw artifactError(artifact.artifactId, "subagent_name", "is missing; this conversation cannot be
|
|
468
|
+
throw artifactError(artifact.artifactId, "subagent_name", "is missing; this conversation cannot be forked");
|
|
469
469
|
if (!artifact.launchSnapshot)
|
|
470
|
-
throw artifactError(artifact.artifactId, RESPONSE_HISTORY_LAUNCH_SNAPSHOT_KEY, "is missing; this conversation cannot be
|
|
470
|
+
throw artifactError(artifact.artifactId, RESPONSE_HISTORY_LAUNCH_SNAPSHOT_KEY, "is missing; this conversation cannot be forked");
|
|
471
471
|
return artifact;
|
|
472
472
|
}
|
|
473
473
|
export async function nextReviewResponseNumber(cwd) {
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { FlowError } from "../flow-error.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
export const VERIFICATION_EXECUTORS = ["bash", "zsh", "pwsh"];
|
|
4
|
+
export const verificationDeclarationSchema = z.object({
|
|
5
|
+
executor: z.enum(VERIFICATION_EXECUTORS),
|
|
6
|
+
script: z.string().refine((value) => value.trim().length > 0, "expected a nonblank script"),
|
|
7
|
+
}).strict();
|
|
8
|
+
function example(heading) {
|
|
9
|
+
return `${heading}\n\`\`\`bash\n<verification script>\n\`\`\``;
|
|
10
|
+
}
|
|
11
|
+
function invalid(options, detail) {
|
|
12
|
+
throw new FlowError("INVALID_KEIYAKU_DRAFT", `${options.heading} ${detail}. Use a fenced executor script block:\n${example(options.heading)}`);
|
|
13
|
+
}
|
|
14
|
+
function isExecutor(value) {
|
|
15
|
+
return VERIFICATION_EXECUTORS.includes(value);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Parse the sole persisted contract Verification declaration grammar. Bodies
|
|
19
|
+
* are deliberately source-level so shell backticks and blank lines stay exact.
|
|
20
|
+
*/
|
|
21
|
+
export function parseVerificationDeclarations(source, options) {
|
|
22
|
+
const lines = source.split(/\r?\n/);
|
|
23
|
+
const declarations = [];
|
|
24
|
+
let sawNone = false;
|
|
25
|
+
for (let index = 0; index < lines.length;) {
|
|
26
|
+
const line = lines[index];
|
|
27
|
+
if (line.trim().length === 0) {
|
|
28
|
+
index += 1;
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (options.allowNone && line.trim() === "- (none)") {
|
|
32
|
+
if (declarations.length > 0 || sawNone) {
|
|
33
|
+
invalid(options, "the explicit '- (none)' replacement cannot be combined with Verification declarations");
|
|
34
|
+
}
|
|
35
|
+
sawNone = true;
|
|
36
|
+
index += 1;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
const opening = line.match(/^ {0,3}(`{3,}|~{3,})(.*)$/);
|
|
40
|
+
if (!opening)
|
|
41
|
+
invalid(options, "must contain only direct fenced script blocks");
|
|
42
|
+
const fence = opening[1];
|
|
43
|
+
const delimiter = fence[0];
|
|
44
|
+
const executor = opening[2].trim();
|
|
45
|
+
if (!executor)
|
|
46
|
+
invalid(options, "fenced script blocks require an executor header (bash, zsh, or pwsh)");
|
|
47
|
+
if (!isExecutor(executor))
|
|
48
|
+
invalid(options, `does not support the '${executor}' executor`);
|
|
49
|
+
const body = [];
|
|
50
|
+
index += 1;
|
|
51
|
+
let closed = false;
|
|
52
|
+
while (index < lines.length) {
|
|
53
|
+
const candidate = lines[index];
|
|
54
|
+
const close = candidate.match(/^ {0,3}(`+|~+)[ \t]*$/);
|
|
55
|
+
if (close && close[1][0] === delimiter && close[1].length >= fence.length) {
|
|
56
|
+
closed = true;
|
|
57
|
+
index += 1;
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
body.push(candidate);
|
|
61
|
+
index += 1;
|
|
62
|
+
}
|
|
63
|
+
if (!closed)
|
|
64
|
+
invalid(options, "contains an unclosed fenced script block");
|
|
65
|
+
const script = body.join("\n");
|
|
66
|
+
if (script.trim().length === 0)
|
|
67
|
+
invalid(options, "contains an empty fenced script block");
|
|
68
|
+
if (sawNone) {
|
|
69
|
+
invalid(options, "the explicit '- (none)' replacement cannot be combined with Verification declarations");
|
|
70
|
+
}
|
|
71
|
+
declarations.push({ executor, script });
|
|
72
|
+
}
|
|
73
|
+
if (sawNone)
|
|
74
|
+
return [];
|
|
75
|
+
if (declarations.length === 0)
|
|
76
|
+
invalid(options, "must contain at least one fenced script block");
|
|
77
|
+
return declarations;
|
|
78
|
+
}
|
|
79
|
+
function fenceFor(executor, script) {
|
|
80
|
+
const candidateLengths = ["`", "~"].map((delimiter) => {
|
|
81
|
+
const largestRun = script.split("\n").reduce((largest, line) => {
|
|
82
|
+
const match = line.match(new RegExp(`^ {0,3}\\${delimiter}+`));
|
|
83
|
+
return Math.max(largest, match?.[0].trim().length ?? 0);
|
|
84
|
+
}, 0);
|
|
85
|
+
return { delimiter, length: Math.max(3, largestRun + 1) };
|
|
86
|
+
});
|
|
87
|
+
const selected = candidateLengths.sort((left, right) => left.length - right.length)[0];
|
|
88
|
+
return selected.delimiter.repeat(selected.length);
|
|
89
|
+
}
|
|
90
|
+
export function renderVerificationDeclarations(declarations) {
|
|
91
|
+
return declarations.map((declaration) => {
|
|
92
|
+
const fence = fenceFor(declaration.executor, declaration.script);
|
|
93
|
+
return `${fence}${declaration.executor}\n${declaration.script}\n${fence}`;
|
|
94
|
+
}).join("\n\n");
|
|
95
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spawn as nodeSpawn } from "node:child_process";
|
|
2
|
-
import { terminateProcessGroup } from "./process-
|
|
2
|
+
import { terminateProcessGroup } from "./run-control/process-tree.js";
|
|
3
3
|
export const WORKTREE_BOOTSTRAP_OUTPUT_TAIL_BYTES = 2048;
|
|
4
4
|
export const WORKTREE_BOOTSTRAP_KILL_GRACE_MS = 10_000;
|
|
5
5
|
function defaultWait(ms) {
|
|
@@ -101,23 +101,19 @@ export function worktreeContainsCoordinate(root, candidate) {
|
|
|
101
101
|
}
|
|
102
102
|
export async function stableRepoRoot(cwd) {
|
|
103
103
|
const git = createGit(cwd);
|
|
104
|
-
let
|
|
104
|
+
let facts;
|
|
105
105
|
try {
|
|
106
|
-
|
|
106
|
+
facts = (await git.raw(["rev-parse", "--is-bare-repository", "--path-format=absolute", "--git-common-dir"])).trim().split("\n");
|
|
107
107
|
}
|
|
108
108
|
catch (error) {
|
|
109
|
-
throw wrapGitError("rev-parse
|
|
109
|
+
throw wrapGitError("rev-parse repository coordinate", error, cwd);
|
|
110
110
|
}
|
|
111
|
+
const [bare, commonDir] = facts;
|
|
111
112
|
if (bare === "true") {
|
|
112
113
|
throw new FlowError("INTERNAL_STATE", "Keiyaku worktree operations require a non-bare repository");
|
|
113
114
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
commonDir = (await git.raw(["rev-parse", "--path-format=absolute", "--git-common-dir"])).trim();
|
|
117
|
-
}
|
|
118
|
-
catch (error) {
|
|
119
|
-
throw wrapGitError("rev-parse --path-format=absolute --git-common-dir", error, cwd);
|
|
120
|
-
}
|
|
115
|
+
if (!commonDir)
|
|
116
|
+
throw new FlowError("INTERNAL_STATE", "Git did not report its common directory");
|
|
121
117
|
const stableRoot = gitCommonRootPath(commonDir);
|
|
122
118
|
if (!path.isAbsolute(stableRoot)) {
|
|
123
119
|
throw new FlowError("INTERNAL_STATE", `git common directory is not absolute: ${commonDir}`);
|
|
@@ -132,7 +128,7 @@ export async function stableRepoRoot(cwd) {
|
|
|
132
128
|
export async function contractWorktreePathFromBind(cwd, contractId, bind) {
|
|
133
129
|
return contractWorktreePathFromStableRoot(await stableRepoRoot(cwd), contractId, bind);
|
|
134
130
|
}
|
|
135
|
-
function contractWorktreePathFromStableRoot(stableRoot, contractId, bind) {
|
|
131
|
+
export function contractWorktreePathFromStableRoot(stableRoot, contractId, bind) {
|
|
136
132
|
return path.join(stableRoot, ".keiyaku", "wt", bind?.data.place ?? contractId);
|
|
137
133
|
}
|
|
138
134
|
export async function contractWorktreePath(cwd, contractId) {
|
package/build/flow-error.js
CHANGED
|
@@ -55,6 +55,12 @@ export function resolveFlowErrorHints(code, context, facts) {
|
|
|
55
55
|
if (facts?.kind === "projection_wake_failure") {
|
|
56
56
|
return [];
|
|
57
57
|
}
|
|
58
|
+
if (facts?.kind === "task_contract_workspace") {
|
|
59
|
+
return ["Select an active contract with a materialized workspace, or retry from the desired checkout with -C."];
|
|
60
|
+
}
|
|
61
|
+
if (facts?.kind === "carrier_dispatch_refusal") {
|
|
62
|
+
return [facts.recoveryCommand];
|
|
63
|
+
}
|
|
58
64
|
switch (code) {
|
|
59
65
|
case "NOT_GIT_REPO":
|
|
60
66
|
return ["The provided `cwd` is not a git repository."];
|
|
@@ -64,10 +70,17 @@ export function resolveFlowErrorHints(code, context, facts) {
|
|
|
64
70
|
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
71
|
case "CONTRACT_AMBIGUOUS":
|
|
66
72
|
return ["Address a specific contract workspace, or use `call --bare` to run without contract attachment."];
|
|
73
|
+
case "CONTRACT_WORKSPACE_UNAVAILABLE":
|
|
74
|
+
return [];
|
|
67
75
|
case "TASK_AMBIGUOUS":
|
|
68
76
|
return ["Pass one complete task coordinate from the listed candidates."];
|
|
69
77
|
case "UNKNOWN_COMMAND":
|
|
70
78
|
return ["Run `keiyaku --help` to inspect available commands."];
|
|
79
|
+
case "UNKNOWN_OPTION":
|
|
80
|
+
case "UNKNOWN_ADDRESS":
|
|
81
|
+
case "INVALID_USAGE":
|
|
82
|
+
case "STDIN_EMPTY":
|
|
83
|
+
return [];
|
|
71
84
|
case "EMPTY_PARAM":
|
|
72
85
|
return ["One or more required parameters are empty."];
|
|
73
86
|
case "DIRTY_WORKTREE":
|
|
@@ -95,7 +108,7 @@ export function resolveFlowErrorHints(code, context, facts) {
|
|
|
95
108
|
case "FROM_DRAFT_NOT_FOUND":
|
|
96
109
|
return ["The path in `from_draft` does not exist. `from_draft` must reference an existing file path (absolute or relative to `cwd`)."];
|
|
97
110
|
case "INVALID_KEIYAKU_DRAFT":
|
|
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`)."];
|
|
111
|
+
return ["Invalid draft/input. Use a `# <title>` heading with non-empty `## Context`, `## Objective`, `## Design & Approach`, `## Scope`, `## Criteria`, and `## Verification` sections (or the equivalent shape in `from_draft`)."];
|
|
99
112
|
case "BIND_TASK_INCOMPLETE":
|
|
100
113
|
return ["Task supplies title/objective defaults; pipe only the missing sections as partial contract Markdown on stdin."];
|
|
101
114
|
case "KEIYAKU_FILE_EXISTS":
|
|
@@ -103,7 +116,7 @@ export function resolveFlowErrorHints(code, context, facts) {
|
|
|
103
116
|
case "DETACHED_HEAD":
|
|
104
117
|
return ["Repository is in detached HEAD state. Address an active contract workspace before retrying."];
|
|
105
118
|
case "INVALID_RESPONSE_PATH":
|
|
106
|
-
return ["`keiyaku
|
|
119
|
+
return ["`keiyaku fork ARTIFACT_ID` needs a valid previous Keiyaku response artifact that includes complete provenance plus `session` and `subagent_name`."];
|
|
107
120
|
case "INVALID_CLOSE_INTENT":
|
|
108
121
|
return ["Invalid close intent. Valid values are `CLAIM` and `FORFEIT`."];
|
|
109
122
|
case "INVALID_SETTINGS":
|
|
@@ -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.13";
|
|
3
|
+
export const GIT_HASH = "0971934";
|