@astrosheep/keiyaku 2.9.2 → 2.9.4
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/build/.tsbuildinfo +1 -1
- package/build/agents/call-terms.js +13 -94
- package/build/agents/harness/activity-values.js +16 -0
- package/build/agents/harness/event-persistence.js +218 -0
- package/build/agents/harness/execution-handle.js +25 -222
- package/build/agents/harness/index.js +3 -1
- package/build/agents/harness/outcome.js +47 -4
- package/build/agents/harness/projection.js +42 -59
- package/build/agents/harness/provider-adapter.js +58 -0
- package/build/agents/harness/router.js +11 -187
- package/build/agents/harness/runtime.js +17 -4
- package/build/agents/harness/session.js +15 -0
- package/build/agents/launch-snapshot/model.js +15 -236
- package/build/agents/launch-snapshot/parse-helpers.js +114 -0
- package/build/agents/launch-snapshot/resolve.js +17 -43
- package/build/agents/opencode-sdk.js +45 -12
- package/build/agents/providers/claude-agent-sdk/adapter.js +6 -17
- package/build/agents/providers/claude-agent-sdk/registration.js +214 -0
- package/build/agents/providers/claude-agent-sdk/session-schema.js +2 -0
- package/build/agents/providers/codex-app-server/adapter.js +50 -53
- package/build/agents/providers/codex-app-server/events.js +67 -11
- package/build/agents/providers/codex-app-server/identity.js +1 -0
- package/build/agents/providers/codex-app-server/policy.js +29 -0
- package/build/agents/providers/codex-app-server/registration.js +248 -0
- package/build/agents/providers/codex-app-server/session-schema.js +6 -0
- package/build/agents/providers/codex-app-server/session.js +23 -17
- package/build/agents/providers/codex-app-server/terms.js +1 -0
- package/build/agents/providers/opencode-sdk/adapter.js +94 -50
- package/build/agents/providers/opencode-sdk/events.js +8 -4
- package/build/agents/providers/opencode-sdk/registration.js +135 -0
- package/build/agents/providers/opencode-sdk/session-schema.js +2 -0
- package/build/agents/providers/opencode-sdk/session.js +21 -14
- package/build/agents/providers/opencode-sdk/terms.js +1 -0
- package/build/agents/providers/pi/adapter.js +36 -30
- package/build/agents/providers/pi/registration.js +145 -0
- package/build/agents/providers/pi/session-schema.js +6 -0
- package/build/agents/providers/pi/terms.js +1 -0
- package/build/agents/providers/registration.js +7 -0
- package/build/agents/providers/registry.js +76 -0
- package/build/agents/selector.js +2 -29
- package/build/agents/types.js +8 -0
- package/build/artifact-directories.js +22 -0
- package/build/cli/commands/akuma/akuma/handler.js +1 -1
- package/build/cli/commands/akuma/view/handler.js +5 -4
- package/build/cli/commands/akuma.js +6 -0
- package/build/cli/commands/contract/amend/handler.js +8 -2
- package/build/cli/commands/contract/arc/handler.js +7 -1
- package/build/cli/commands/contract/bind/handler.js +4 -3
- package/build/cli/commands/contract/bind/meta.js +34 -7
- package/build/cli/commands/contract/forfeit/handler.js +4 -4
- package/build/cli/commands/contract/log/handler.js +3 -3
- package/build/cli/commands/contract/petition/handler.js +4 -6
- package/build/cli/commands/contract/renew/handler.js +20 -6
- package/build/cli/commands/contract/renew/meta.js +5 -4
- package/build/cli/commands/metadata.js +58 -0
- package/build/cli/commands/projection/call/handler.js +6 -5
- package/build/cli/commands/projection/kill/handler.js +16 -32
- package/build/cli/commands/projection/shared.js +28 -0
- package/build/cli/commands/projection/status/handler.js +1 -1
- package/build/cli/commands/projection/tell/handler.js +14 -30
- package/build/cli/commands/projection/wait/handler.js +45 -22
- package/build/cli/commands/projection/wait/meta.js +7 -2
- package/build/cli/commands/registry.js +2 -54
- package/build/cli/commands/shared.js +10 -7
- package/build/cli/commands/skills/install/handler.js +2 -5
- package/build/cli/commands/skills/skills/handler.js +1 -1
- package/build/cli/commands/task/add/handler.js +5 -13
- package/build/cli/commands/task/catalog.js +2 -2
- package/build/cli/commands/task/done/handler.js +4 -8
- package/build/cli/commands/task/drop/handler.js +3 -6
- package/build/cli/commands/task/ls/handler.js +3 -5
- package/build/cli/commands/task/shared.js +14 -4
- package/build/cli/commands/task/{view → show}/handler.js +7 -8
- package/build/cli/commands/task/show/meta.js +8 -0
- package/build/cli/commands/task/start/handler.js +3 -6
- package/build/cli/commands/task/stop/handler.js +4 -8
- package/build/cli/commands/task/task/handler.js +1 -1
- package/build/cli/commands/task/update/handler.js +4 -8
- package/build/cli/completion.js +1 -1
- package/build/cli/flags.js +19 -3
- package/build/cli/help.js +4 -11
- package/build/cli/index.js +57 -83
- package/build/cli/missing-subcommand.js +7 -0
- package/build/cli/parse-flags.js +14 -5
- package/build/cli/parse-metadata.js +3 -3
- package/build/cli/parse-selectors.js +1 -1
- package/build/cli/parse.js +21 -0
- package/build/cli/projection-address.js +7 -12
- package/build/cli/render/address.js +41 -37
- package/build/cli/render/arc.js +24 -12
- package/build/cli/render/call.js +11 -5
- package/build/cli/render/errors.js +36 -1
- package/build/cli/render/format.js +4 -0
- package/build/cli/render/kanshi.js +6 -12
- package/build/cli/render/kill.js +9 -0
- package/build/cli/render/misc.js +55 -12
- package/build/cli/render/petition.js +20 -15
- package/build/cli/render/projection-activity.js +3 -0
- package/build/cli/render/shared.js +60 -21
- package/build/cli/render/skills.js +10 -0
- package/build/cli/render/status.js +37 -14
- package/build/cli/render/success-response.js +77 -0
- package/build/cli/render/task.js +54 -14
- package/build/cli/render/tell.js +5 -0
- package/build/cli/render/terminal-failure.js +58 -0
- package/build/cli/render/types.js +1 -0
- package/build/cli/render/wait.js +38 -36
- package/build/cli/types.js +1 -1
- package/build/config/akuma-loader.js +0 -5
- package/build/config/env-keys.js +1 -1
- package/build/config/provider-policy-ownership.js +3 -32
- package/build/config/settings/disease.js +8 -1
- package/build/config/settings/loader.js +11 -3
- package/build/config/settings/provider-instance-fields.js +51 -0
- package/build/config/settings/schema.js +6 -176
- package/build/core/addressing.js +66 -16
- package/build/core/amend.js +135 -18
- package/build/core/arc.js +19 -67
- package/build/core/bind.js +82 -35
- package/build/core/call/call.js +15 -9
- package/build/core/call/context.js +6 -51
- package/build/core/call/execution.js +21 -22
- package/build/core/call/prompt.js +14 -1
- package/build/core/command-markdown.js +45 -0
- package/build/core/context.js +13 -7
- package/build/core/contract-view.js +20 -3
- package/build/core/draft.js +123 -16
- package/build/core/forfeit.js +5 -2
- package/build/core/hints.js +1 -75
- package/build/core/identifier-slug.js +66 -0
- package/build/core/ids.js +25 -11
- package/build/core/ledger.js +20 -68
- package/build/core/markdown/parser.js +0 -68
- package/build/core/markdown/render.js +68 -0
- package/build/core/markdown/sections.js +2 -1
- package/build/core/outcome-base.js +4 -0
- package/build/core/projection/generation/database.js +2 -38
- package/build/core/projection/generation/model.js +42 -1
- package/build/core/{projection-generation-continuation.js → projection/generation/projection-generation-continuation.js} +3 -3
- package/build/core/{projection-generation-execution.js → projection/generation/projection-generation-execution.js} +19 -49
- package/build/core/{projection-generation-identity.js → projection/generation/projection-generation-identity.js} +2 -2
- package/build/core/{projection-generation-launcher.js → projection/generation/projection-generation-launcher.js} +5 -3
- package/build/core/{projection-generation-process.js → projection/generation/projection-generation-process.js} +2 -1
- package/build/core/{projection-generation-runner.js → projection/generation/projection-generation-runner.js} +18 -3
- package/build/core/projection/generation/store.js +2 -2
- package/build/core/projection/generation/transitions.js +1 -1
- package/build/core/projection/index.js +27 -0
- package/build/core/{projection-activity.js → projection/projection-activity.js} +12 -0
- package/build/core/{projection-alias.js → projection/projection-alias.js} +14 -19
- package/build/core/projection/projection-coordinate.js +61 -0
- package/build/core/{projection-core.js → projection/projection-core.js} +9 -9
- package/build/core/{projection-execution-observer.js → projection/projection-execution-observer.js} +2 -57
- package/build/core/{projection-identity.js → projection/projection-identity.js} +1 -1
- package/build/core/{projection-kill.js → projection/projection-kill.js} +1 -1
- package/build/core/{projection-life-observer.js → projection/projection-life-observer.js} +1 -1
- package/build/core/{projection-mint.js → projection/projection-mint.js} +10 -10
- package/build/core/{projection-status.js → projection/projection-status.js} +98 -25
- package/build/core/projection/projection-terminal-failure.js +104 -0
- package/build/core/projection/projection-wait.js +278 -0
- package/build/core/projection/projection-wake.js +203 -0
- package/build/core/provider-loader-ports.js +4 -0
- package/build/core/ref-log.js +65 -40
- package/build/core/registry.js +63 -10
- package/build/core/renew-session.js +128 -0
- package/build/core/renew.js +426 -99
- package/build/core/scope.js +143 -66
- package/build/core/seal.js +139 -35
- package/build/core/{claim-delivery.js → settlement/claim-delivery.js} +9 -14
- package/build/core/{claim.js → settlement/claim.js} +32 -8
- package/build/core/settlement/index.js +19 -0
- package/build/core/{petition-claim-gates.js → settlement/petition-claim-gates.js} +22 -18
- package/build/core/{petition-forfeit.js → settlement/petition-forfeit.js} +2 -2
- package/build/core/{petition-head-guard.js → settlement/petition-head-guard.js} +2 -2
- package/build/core/settlement/petition-io.js +1 -0
- package/build/core/{petition-preview.js → settlement/petition-preview.js} +1 -1
- package/build/core/{petition.js → settlement/petition.js} +12 -10
- package/build/core/settlement/queue-read-model.js +143 -0
- package/build/core/settlement/queue-seat-allocation.js +156 -0
- package/build/core/settlement/queue.js +6 -0
- package/build/core/settlement/settlement.js +295 -0
- package/build/core/{verdict.js → settlement/verdict.js} +9 -37
- package/build/core/status/board.js +24 -23
- package/build/core/status/ledger-batch.js +158 -0
- package/build/core/status/reconciliation.js +10 -4
- package/build/core/stored-agent-event.js +3 -0
- package/build/core/task/board.js +40 -4
- package/build/core/task/commands.js +30 -21
- package/build/core/task/index.js +12 -0
- package/build/core/task/settlement-git.js +157 -65
- package/build/core/task/settlement-policy.js +54 -24
- package/build/core/task/task-contract.js +370 -0
- package/build/core/{task-doctor.js → task/task-doctor.js} +1 -1
- package/build/core/{task-git-actor.js → task/task-git-actor.js} +5 -10
- package/build/core/{task-git-runtime.js → task/task-git-runtime.js} +3 -3
- package/build/core/{task-git-store.js → task/task-git-store.js} +92 -29
- package/build/core/task/task-repository.js +1 -0
- package/build/core/{task-store-repository.js → task/task-store-repository.js} +5 -4
- package/build/core/task/task-store.js +1 -0
- package/build/core/{task-worktree.js → task/task-worktree.js} +2 -1
- package/build/core/task/task.js +5 -0
- package/build/core/task/tree.js +37 -0
- package/build/core/task/validation.js +5 -2
- package/build/core/transcripts.js +15 -3
- package/build/core/worktree-path.js +43 -28
- package/build/flow-error.js +85 -39
- package/build/fs/durability.js +27 -0
- package/build/fs/path-coordinate.js +16 -0
- package/build/generated/version.js +3 -2
- package/build/git/branches.js +2 -1
- package/build/git/core.js +106 -2
- package/build/git/path-coordinate.js +17 -0
- package/build/git/refs.js +57 -0
- package/build/git/streaming-batch.js +301 -0
- package/build/git/worktree.js +65 -0
- package/build/index.js +3 -3
- package/build/keiyaku.js +2 -36
- package/package.json +4 -10
- package/skills/keiyaku/SKILL.md +3 -2
- package/skills/keiyaku-akuma/SKILL.md +10 -6
- package/skills/keiyaku-task/SKILL.md +1 -1
- package/skills/keiyaku-workflow/SKILL.md +25 -4
- package/build/agents/providers/codex-sdk.js +0 -329
- package/build/cli/commands/task/view/meta.js +0 -8
- package/build/core/command-io.js +0 -174
- package/build/core/path-coordinate.js +0 -27
- package/build/core/petition-claim.js +0 -117
- package/build/core/petition-run.js +0 -83
- package/build/core/projection-coordinate.js +0 -63
- package/build/core/projection-wait.js +0 -165
- package/build/core/projection-wake.js +0 -136
- package/build/core/queue.js +0 -343
- package/build/core/task-contract.js +0 -237
- package/build/core/task.js +0 -4
- /package/build/{core/task-repository.js → agents/launch-snapshot/types.js} +0 -0
- /package/build/{core/task-store.js → agents/providers/claude-agent-sdk/terms.js} +0 -0
- /package/build/core/{projection-generation-runtime.js → projection/generation/projection-generation-runtime.js} +0 -0
- /package/build/core/{projection-life-protocol.js → projection/projection-life-protocol.js} +0 -0
- /package/build/core/{projection-runner-lock.js → projection/projection-runner-lock.js} +0 -0
|
@@ -1,19 +1,13 @@
|
|
|
1
|
-
import { spawnSync } from "node:child_process";
|
|
2
1
|
import * as fs from "node:fs";
|
|
3
2
|
import * as path from "node:path";
|
|
4
3
|
import { FlowError } from "../../flow-error.js";
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
input,
|
|
10
|
-
encoding: "utf8",
|
|
11
|
-
env: { ...process.env, ...env, GIT_TERMINAL_PROMPT: "0", LC_ALL: "C" },
|
|
12
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
13
|
-
});
|
|
4
|
+
import { runGitProcess } from "../../git/core.js";
|
|
5
|
+
import { parseTaskDocument } from "./task.js";
|
|
6
|
+
async function gitResult(cwd, args, input, env) {
|
|
7
|
+
return await runGitProcess(cwd, args, { input, env });
|
|
14
8
|
}
|
|
15
|
-
function git(cwd, args, input, env) {
|
|
16
|
-
const result = gitResult(cwd, args, input, env);
|
|
9
|
+
async function git(cwd, args, input, env) {
|
|
10
|
+
const result = await gitResult(cwd, args, input, env);
|
|
17
11
|
if (result.error) {
|
|
18
12
|
throw new FlowError("INTERNAL_STATE", `git ${args[0]} failed: ${result.error.message}`, { cause: result.error });
|
|
19
13
|
}
|
|
@@ -29,36 +23,118 @@ function boardPath(root, source) {
|
|
|
29
23
|
}
|
|
30
24
|
return relative;
|
|
31
25
|
}
|
|
32
|
-
export function inspectCurrentBranch(cwd) {
|
|
33
|
-
const branch = gitResult(cwd, ["symbolic-ref", "--quiet", "HEAD"]);
|
|
26
|
+
export async function inspectCurrentBranch(cwd) {
|
|
27
|
+
const branch = await gitResult(cwd, ["symbolic-ref", "--quiet", "HEAD"]);
|
|
34
28
|
if (branch.error) {
|
|
35
29
|
throw new FlowError("INTERNAL_STATE", `cannot inspect HEAD: ${branch.error.message}`, { cause: branch.error });
|
|
36
30
|
}
|
|
37
31
|
if (branch.status !== 0 || branch.stdout.trim() === "") {
|
|
38
32
|
throw new FlowError("DETACHED_HEAD", "cannot publish task board from detached HEAD");
|
|
39
33
|
}
|
|
40
|
-
return { ref: branch.stdout.trim(), head: git(cwd, ["rev-parse", "HEAD"]).trim() };
|
|
34
|
+
return { ref: branch.stdout.trim(), head: (await git(cwd, ["rev-parse", "HEAD"])).trim() };
|
|
41
35
|
}
|
|
42
|
-
export function createTaskBoardCommit(input) {
|
|
36
|
+
export async function createTaskBoardCommit(input) {
|
|
43
37
|
const temporaryDirectory = fs.mkdtempSync(path.join(input.gitDir, "keiyaku-task-index-"));
|
|
44
38
|
const indexPath = path.join(temporaryDirectory, "index");
|
|
45
39
|
const env = { GIT_INDEX_FILE: indexPath };
|
|
46
40
|
try {
|
|
47
|
-
git(input.cwd, ["read-tree", input.parent], undefined, env);
|
|
48
|
-
git(input.cwd, ["rm", "-r", "--cached", "-f", "--ignore-unmatch", "--", ".keiyaku/tasks"], undefined, env);
|
|
41
|
+
await git(input.cwd, ["read-tree", input.parent], undefined, env);
|
|
49
42
|
for (const source of input.sources) {
|
|
50
|
-
const oid = git(input.cwd, ["hash-object", "-w", "--stdin"], source.text).trim();
|
|
51
|
-
git(input.cwd, ["update-index", "--add", "--cacheinfo", `100644,${oid},${boardPath(input.root, source)}`], undefined, env);
|
|
43
|
+
const oid = (await git(input.cwd, ["hash-object", "-w", "--stdin"], source.text)).trim();
|
|
44
|
+
await git(input.cwd, ["update-index", "--add", "--cacheinfo", `100644,${oid},${boardPath(input.root, source)}`], undefined, env);
|
|
45
|
+
}
|
|
46
|
+
const tree = (await git(input.cwd, ["write-tree"], undefined, env)).trim();
|
|
47
|
+
return (await git(input.cwd, ["commit-tree", tree, "-p", input.parent, "-m", input.subject])).trim();
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
fs.rmSync(temporaryDirectory, { recursive: true, force: true });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function taskMutationPath(write) {
|
|
54
|
+
return `.keiyaku/tasks/${write.id}.md`;
|
|
55
|
+
}
|
|
56
|
+
/** Build a commit that updates only the listed task paths on top of parent. */
|
|
57
|
+
export async function createTaskMutationCommit(input) {
|
|
58
|
+
if (input.writes.length === 0) {
|
|
59
|
+
throw new FlowError("INTERNAL_STATE", "task mutation commit requires at least one write");
|
|
60
|
+
}
|
|
61
|
+
const temporaryDirectory = fs.mkdtempSync(path.join(input.gitDir, "keiyaku-task-index-"));
|
|
62
|
+
const env = { GIT_INDEX_FILE: path.join(temporaryDirectory, "index") };
|
|
63
|
+
try {
|
|
64
|
+
await git(input.cwd, ["read-tree", input.parent], undefined, env);
|
|
65
|
+
for (const write of input.writes) {
|
|
66
|
+
const oid = (await git(input.cwd, ["hash-object", "-w", "--stdin"], write.text)).trim();
|
|
67
|
+
await git(input.cwd, [
|
|
68
|
+
"update-index",
|
|
69
|
+
"--add",
|
|
70
|
+
"--cacheinfo",
|
|
71
|
+
`100644,${oid},${taskMutationPath(write)}`,
|
|
72
|
+
], undefined, env);
|
|
52
73
|
}
|
|
53
|
-
const tree = git(input.cwd, ["write-tree"], undefined, env).trim();
|
|
54
|
-
|
|
74
|
+
const tree = (await git(input.cwd, ["write-tree"], undefined, env)).trim();
|
|
75
|
+
const parentTree = (await git(input.cwd, ["rev-parse", `${input.parent}^{tree}`])).trim();
|
|
76
|
+
if (tree === parentTree)
|
|
77
|
+
return input.parent;
|
|
78
|
+
return (await git(input.cwd, ["commit-tree", tree, "-p", input.parent, "-m", input.subject])).trim();
|
|
55
79
|
}
|
|
56
80
|
finally {
|
|
57
81
|
fs.rmSync(temporaryDirectory, { recursive: true, force: true });
|
|
58
82
|
}
|
|
59
83
|
}
|
|
60
|
-
|
|
61
|
-
|
|
84
|
+
/** Stage only the mutated task paths into the real index; leave unrelated entries alone. */
|
|
85
|
+
export async function reconcileTaskMutationIndex(cwd, writes) {
|
|
86
|
+
for (const write of writes) {
|
|
87
|
+
const oid = (await git(cwd, ["hash-object", "-w", "--stdin"], write.text)).trim();
|
|
88
|
+
await git(cwd, [
|
|
89
|
+
"update-index",
|
|
90
|
+
"--add",
|
|
91
|
+
"--cacheinfo",
|
|
92
|
+
`100644,${oid},${taskMutationPath(write)}`,
|
|
93
|
+
]);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Atomically publish a pathspec-scoped task micro-commit under the hub branch.
|
|
98
|
+
* Unrelated staged and unstaged non-task bytes are never read into the commit.
|
|
99
|
+
*/
|
|
100
|
+
export async function publishTaskMutationCommit(input, deps = {}) {
|
|
101
|
+
let lastError;
|
|
102
|
+
for (let attempt = 0; attempt < 4; attempt += 1) {
|
|
103
|
+
const branch = await inspectCurrentBranch(input.cwd);
|
|
104
|
+
const commit = await createTaskMutationCommit({
|
|
105
|
+
cwd: input.cwd,
|
|
106
|
+
gitDir: input.gitDir,
|
|
107
|
+
parent: branch.head,
|
|
108
|
+
writes: input.writes,
|
|
109
|
+
subject: input.subject,
|
|
110
|
+
});
|
|
111
|
+
if (commit === branch.head) {
|
|
112
|
+
await reconcileTaskMutationIndex(input.cwd, input.writes);
|
|
113
|
+
return commit;
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
await updateBranch({
|
|
117
|
+
cwd: input.cwd,
|
|
118
|
+
ref: branch.ref,
|
|
119
|
+
next: commit,
|
|
120
|
+
expected: branch.head,
|
|
121
|
+
}, deps);
|
|
122
|
+
try {
|
|
123
|
+
await reconcileTaskMutationIndex(input.cwd, input.writes);
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
// Branch fact remains authoritative; index may be repaired by a later mutation.
|
|
127
|
+
}
|
|
128
|
+
return commit;
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
lastError = error;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
throw lastError;
|
|
135
|
+
}
|
|
136
|
+
async function taskTreeEntries(cwd, commit) {
|
|
137
|
+
return (await git(cwd, ["ls-tree", "-r", "-z", commit, "--", ".keiyaku/tasks"]))
|
|
62
138
|
.split("\0")
|
|
63
139
|
.filter(Boolean)
|
|
64
140
|
.map((record) => {
|
|
@@ -70,28 +146,35 @@ function taskTreeEntries(cwd, commit) {
|
|
|
70
146
|
return { mode, oid, path: record.slice(tab + 1) };
|
|
71
147
|
});
|
|
72
148
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
149
|
+
/** Reconcile only settlement-owned task paths; never rewrite an unrelated index entry. */
|
|
150
|
+
export async function reconcileTaskPathsetIndex(cwd, commit, paths) {
|
|
151
|
+
const entries = new Map((await taskTreeEntries(cwd, commit)).map((entry) => [entry.path, entry]));
|
|
152
|
+
for (const taskPath of paths) {
|
|
153
|
+
const entry = entries.get(taskPath);
|
|
154
|
+
if (entry) {
|
|
155
|
+
await git(cwd, ["update-index", "--add", "--cacheinfo", `${entry.mode},${entry.oid},${entry.path}`]);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
await git(cwd, ["update-index", "--force-remove", "--", taskPath]);
|
|
159
|
+
}
|
|
81
160
|
}
|
|
82
161
|
}
|
|
83
|
-
|
|
84
|
-
|
|
162
|
+
/** Task paths tracked by one authoritative commit, independent of the worktree index. */
|
|
163
|
+
export async function trackedTaskPaths(cwd, commit) {
|
|
164
|
+
return (await taskTreeEntries(cwd, commit)).map((entry) => entry.path).sort();
|
|
165
|
+
}
|
|
166
|
+
export async function refHead(cwd, ref) {
|
|
167
|
+
return (await git(cwd, ["rev-parse", ref])).trim();
|
|
85
168
|
}
|
|
86
|
-
export function readableRefHead(cwd, ref) {
|
|
87
|
-
const result = gitResult(cwd, ["rev-parse", ref]);
|
|
169
|
+
export async function readableRefHead(cwd, ref) {
|
|
170
|
+
const result = await gitResult(cwd, ["rev-parse", ref]);
|
|
88
171
|
if (result.status !== 0)
|
|
89
172
|
return undefined;
|
|
90
173
|
return result.stdout.trim();
|
|
91
174
|
}
|
|
92
|
-
export function taskContractAtCommit(cwd, commit, id) {
|
|
175
|
+
export async function taskContractAtCommit(cwd, commit, id) {
|
|
93
176
|
const sourcePath = `.keiyaku/tasks/${id}.md`;
|
|
94
|
-
const result = gitResult(cwd, ["show", `${commit}:${sourcePath}`]);
|
|
177
|
+
const result = await gitResult(cwd, ["show", `${commit}:${sourcePath}`]);
|
|
95
178
|
if (result.status !== 0)
|
|
96
179
|
return undefined;
|
|
97
180
|
return parseTaskDocument({ path: sourcePath, text: result.stdout }).contractId;
|
|
@@ -100,17 +183,17 @@ export async function updateBranch(input, deps) {
|
|
|
100
183
|
if (deps.updateBranch)
|
|
101
184
|
await deps.updateBranch(input);
|
|
102
185
|
else
|
|
103
|
-
git(input.cwd, ["update-ref", input.ref, input.next, input.expected]);
|
|
186
|
+
await git(input.cwd, ["update-ref", input.ref, input.next, input.expected]);
|
|
104
187
|
}
|
|
105
188
|
export async function publishForwardTaskBoardCommit(input, deps) {
|
|
106
189
|
let lastError;
|
|
107
190
|
for (let attempt = 0; attempt < 4; attempt += 1) {
|
|
108
|
-
const parent = refHead(input.cwd, input.ref);
|
|
109
|
-
const commit = createTaskBoardCommit({ ...input, parent });
|
|
191
|
+
const parent = await refHead(input.cwd, input.ref);
|
|
192
|
+
const commit = await createTaskBoardCommit({ ...input, parent });
|
|
110
193
|
try {
|
|
111
194
|
await updateBranch({ cwd: input.cwd, ref: input.ref, next: commit, expected: parent }, deps);
|
|
112
195
|
try {
|
|
113
|
-
|
|
196
|
+
await reconcileTaskPathsetIndex(input.cwd, commit, input.paths);
|
|
114
197
|
}
|
|
115
198
|
catch { /* the forward branch fact remains authoritative */ }
|
|
116
199
|
return commit;
|
|
@@ -121,32 +204,32 @@ export async function publishForwardTaskBoardCommit(input, deps) {
|
|
|
121
204
|
}
|
|
122
205
|
throw lastError;
|
|
123
206
|
}
|
|
124
|
-
function treeWithoutTasks(cwd, commit, gitDir) {
|
|
207
|
+
async function treeWithoutTasks(cwd, commit, gitDir) {
|
|
125
208
|
const temporaryDirectory = fs.mkdtempSync(path.join(gitDir, "keiyaku-landing-index-"));
|
|
126
209
|
const env = { GIT_INDEX_FILE: path.join(temporaryDirectory, "index") };
|
|
127
210
|
try {
|
|
128
|
-
git(cwd, ["read-tree", commit], undefined, env);
|
|
129
|
-
git(cwd, ["rm", "-r", "--cached", "-f", "--ignore-unmatch", "--", ".keiyaku/tasks"], undefined, env);
|
|
130
|
-
return git(cwd, ["write-tree"], undefined, env).trim();
|
|
211
|
+
await git(cwd, ["read-tree", commit], undefined, env);
|
|
212
|
+
await git(cwd, ["rm", "-r", "--cached", "-f", "--ignore-unmatch", "--", ".keiyaku/tasks"], undefined, env);
|
|
213
|
+
return (await git(cwd, ["write-tree"], undefined, env)).trim();
|
|
131
214
|
}
|
|
132
215
|
finally {
|
|
133
216
|
fs.rmSync(temporaryDirectory, { recursive: true, force: true });
|
|
134
217
|
}
|
|
135
218
|
}
|
|
136
|
-
function composeLandingTree(input) {
|
|
137
|
-
const baseTree = treeWithoutTasks(input.cwd, input.base, input.gitDir);
|
|
138
|
-
const targetTree = treeWithoutTasks(input.cwd, input.target, input.gitDir);
|
|
139
|
-
const deliveryTree = treeWithoutTasks(input.cwd, input.delivery, input.gitDir);
|
|
219
|
+
async function composeLandingTree(input) {
|
|
220
|
+
const baseTree = await treeWithoutTasks(input.cwd, input.base, input.gitDir);
|
|
221
|
+
const targetTree = await treeWithoutTasks(input.cwd, input.target, input.gitDir);
|
|
222
|
+
const deliveryTree = await treeWithoutTasks(input.cwd, input.delivery, input.gitDir);
|
|
140
223
|
let mergedTree;
|
|
141
224
|
try {
|
|
142
|
-
mergedTree = git(input.cwd, [
|
|
225
|
+
mergedTree = (await git(input.cwd, [
|
|
143
226
|
"merge-tree",
|
|
144
227
|
"--write-tree",
|
|
145
228
|
"--merge-base",
|
|
146
229
|
baseTree,
|
|
147
230
|
targetTree,
|
|
148
231
|
deliveryTree,
|
|
149
|
-
]).trim();
|
|
232
|
+
])).trim();
|
|
150
233
|
}
|
|
151
234
|
catch (error) {
|
|
152
235
|
throw new FlowError("CLOSE_MERGE_CONFLICT", "claim merge conflict outside task board", { cause: error });
|
|
@@ -154,26 +237,35 @@ function composeLandingTree(input) {
|
|
|
154
237
|
const temporaryDirectory = fs.mkdtempSync(path.join(input.gitDir, "keiyaku-landing-index-"));
|
|
155
238
|
const env = { GIT_INDEX_FILE: path.join(temporaryDirectory, "index") };
|
|
156
239
|
try {
|
|
157
|
-
git(input.cwd, ["read-tree", mergedTree], undefined, env);
|
|
158
|
-
|
|
240
|
+
await git(input.cwd, ["read-tree", mergedTree], undefined, env);
|
|
241
|
+
// merge-tree deliberately excluded task paths. Restore the target board as
|
|
242
|
+
// the inherited baseline, then overlay only settlement-authorized writes.
|
|
243
|
+
for (const entry of await taskTreeEntries(input.cwd, input.target)) {
|
|
244
|
+
await git(input.cwd, [
|
|
245
|
+
"update-index",
|
|
246
|
+
"--add",
|
|
247
|
+
"--cacheinfo",
|
|
248
|
+
`${entry.mode},${entry.oid},${entry.path}`,
|
|
249
|
+
], undefined, env);
|
|
250
|
+
}
|
|
159
251
|
for (const source of input.sources) {
|
|
160
|
-
const oid = git(input.cwd, ["hash-object", "-w", "--stdin"], source.text).trim();
|
|
161
|
-
git(input.cwd, [
|
|
252
|
+
const oid = (await git(input.cwd, ["hash-object", "-w", "--stdin"], source.text)).trim();
|
|
253
|
+
await git(input.cwd, [
|
|
162
254
|
"update-index",
|
|
163
255
|
"--add",
|
|
164
256
|
"--cacheinfo",
|
|
165
257
|
`100644,${oid},${boardPath(input.root, source)}`,
|
|
166
258
|
], undefined, env);
|
|
167
259
|
}
|
|
168
|
-
return git(input.cwd, ["write-tree"], undefined, env).trim();
|
|
260
|
+
return (await git(input.cwd, ["write-tree"], undefined, env)).trim();
|
|
169
261
|
}
|
|
170
262
|
finally {
|
|
171
263
|
fs.rmSync(temporaryDirectory, { recursive: true, force: true });
|
|
172
264
|
}
|
|
173
265
|
}
|
|
174
|
-
export function createLandingCommit(input) {
|
|
175
|
-
const tree = composeLandingTree(input);
|
|
176
|
-
return git(input.cwd, [
|
|
266
|
+
export async function createLandingCommit(input) {
|
|
267
|
+
const tree = await composeLandingTree(input);
|
|
268
|
+
return (await git(input.cwd, [
|
|
177
269
|
"commit-tree",
|
|
178
270
|
tree,
|
|
179
271
|
"-p",
|
|
@@ -184,13 +276,13 @@ export function createLandingCommit(input) {
|
|
|
184
276
|
`keiyaku: claim ${input.contractId}`,
|
|
185
277
|
"-m",
|
|
186
278
|
`Merge contract ${input.contractId} into ${input.targetBranch}.`,
|
|
187
|
-
]).trim();
|
|
279
|
+
])).trim();
|
|
188
280
|
}
|
|
189
|
-
export function containsCommit(cwd, ancestor, descendant) {
|
|
190
|
-
return gitResult(cwd, ["merge-base", "--is-ancestor", ancestor, descendant]).status === 0;
|
|
281
|
+
export async function containsCommit(cwd, ancestor, descendant) {
|
|
282
|
+
return (await gitResult(cwd, ["merge-base", "--is-ancestor", ancestor, descendant])).status === 0;
|
|
191
283
|
}
|
|
192
|
-
export function updateTargetRef(cwd, ref, next, expected) {
|
|
193
|
-
git(cwd, ["update-ref", ref, next, expected]);
|
|
284
|
+
export async function updateTargetRef(cwd, ref, next, expected) {
|
|
285
|
+
await git(cwd, ["update-ref", ref, next, expected]);
|
|
194
286
|
}
|
|
195
287
|
export function sameTaskSourceBytes(root, left, right) {
|
|
196
288
|
const snapshot = (sources) => new Map(sources.map((source) => [boardPath(root, source), source.text]));
|
|
@@ -31,15 +31,12 @@ export function reopenedTaskDocument(task) {
|
|
|
31
31
|
delete document.startedAt;
|
|
32
32
|
return document;
|
|
33
33
|
}
|
|
34
|
-
|
|
35
|
-
requireHealthySettlementBoard(board);
|
|
36
|
-
const id = resolveTaskReadId(board, taskId);
|
|
37
|
-
const task = board.byId.get(id);
|
|
34
|
+
function validateReadyForBind(board, task) {
|
|
38
35
|
if (task.state === "in_progress") {
|
|
39
|
-
throw new FlowError("INVALID_TARGET", `task ${id} is in_progress; stop it before bind`);
|
|
36
|
+
throw new FlowError("INVALID_TARGET", `task ${task.id} is in_progress; stop it before bind`);
|
|
40
37
|
}
|
|
41
38
|
if (task.state !== "open") {
|
|
42
|
-
throw new FlowError("INVALID_TARGET", `task ${id} is ${task.state}, not ready for bind`);
|
|
39
|
+
throw new FlowError("INVALID_TARGET", `task ${task.id} is ${task.state}, not ready for bind`);
|
|
43
40
|
}
|
|
44
41
|
const unsatisfied = task.needs
|
|
45
42
|
.filter((need) => {
|
|
@@ -48,22 +45,64 @@ export function selectReadyTaskForBind(board, taskId) {
|
|
|
48
45
|
})
|
|
49
46
|
.sort();
|
|
50
47
|
if (unsatisfied.length > 0) {
|
|
51
|
-
throw new FlowError("INVALID_TARGET", `task ${id} is blocked by: ${unsatisfied.join(", ")}`);
|
|
48
|
+
throw new FlowError("INVALID_TARGET", `task ${task.id} is blocked by: ${unsatisfied.join(", ")}`);
|
|
52
49
|
}
|
|
53
50
|
if (task.contractId !== undefined) {
|
|
54
|
-
throw new FlowError("INVALID_TARGET", `task ${id} is already bound to contract ${task.contractId}`);
|
|
51
|
+
throw new FlowError("INVALID_TARGET", `task ${task.id} is already bound to contract ${task.contractId}`);
|
|
55
52
|
}
|
|
56
|
-
return task;
|
|
57
53
|
}
|
|
58
|
-
export function
|
|
54
|
+
export function selectReadyTasksForBind(board, taskIds) {
|
|
59
55
|
requireHealthySettlementBoard(board);
|
|
60
|
-
const
|
|
56
|
+
const rootIds = taskIds.map((taskId) => resolveTaskReadId(board, taskId));
|
|
57
|
+
const duplicates = rootIds.filter((id, index) => rootIds.indexOf(id) !== index);
|
|
58
|
+
if (duplicates.length > 0) {
|
|
59
|
+
throw new FlowError("INVALID_TARGET", `task ${[...new Set(duplicates)].sort().join(", ")} selected more than once`);
|
|
60
|
+
}
|
|
61
|
+
const admitted = new Set();
|
|
62
|
+
const tasks = [];
|
|
63
|
+
for (const rootId of rootIds) {
|
|
64
|
+
const root = board.byId.get(rootId);
|
|
65
|
+
if (!admitted.has(root.id)) {
|
|
66
|
+
admitted.add(root.id);
|
|
67
|
+
tasks.push(root);
|
|
68
|
+
}
|
|
69
|
+
for (const candidate of board.tasks) {
|
|
70
|
+
if (candidate.parent === null || admitted.has(candidate.id))
|
|
71
|
+
continue;
|
|
72
|
+
if (candidate.state === "done" || candidate.state === "drop")
|
|
73
|
+
continue;
|
|
74
|
+
if (isDescendantOf(board, candidate, rootId)) {
|
|
75
|
+
admitted.add(candidate.id);
|
|
76
|
+
tasks.push(candidate);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
for (const task of tasks)
|
|
81
|
+
validateReadyForBind(board, task);
|
|
82
|
+
return tasks;
|
|
83
|
+
}
|
|
84
|
+
function isDescendantOf(board, task, ancestorId) {
|
|
85
|
+
let current = task.parent;
|
|
86
|
+
while (current !== null) {
|
|
87
|
+
if (current === ancestorId)
|
|
88
|
+
return true;
|
|
89
|
+
current = board.byId.get(current)?.parent ?? null;
|
|
90
|
+
}
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
export function tasksForContract(board, contractId) {
|
|
94
|
+
requireHealthySettlementBoard(board);
|
|
95
|
+
return board.tasks
|
|
61
96
|
.filter((task) => task.contractId === contractId)
|
|
62
97
|
.sort((left, right) => left.id.localeCompare(right.id));
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
98
|
+
}
|
|
99
|
+
export function doneTaskReplacements(board, contractId) {
|
|
100
|
+
return tasksForContract(board, contractId).map((task) => {
|
|
101
|
+
const settled = settlementDocument(task, undefined);
|
|
102
|
+
settled.state = "done";
|
|
103
|
+
delete settled.startedAt;
|
|
104
|
+
return settled;
|
|
105
|
+
});
|
|
67
106
|
}
|
|
68
107
|
export function requireSettledTask(board, id) {
|
|
69
108
|
const task = requireHealthySettlementBoard(board).byId.get(id);
|
|
@@ -71,12 +110,3 @@ export function requireSettledTask(board, id) {
|
|
|
71
110
|
throw new FlowError("INTERNAL_STATE", `task ${id} disappeared from the board`);
|
|
72
111
|
return task;
|
|
73
112
|
}
|
|
74
|
-
export function doneTaskReplacement(board, contractId) {
|
|
75
|
-
const bound = taskForContract(board, contractId);
|
|
76
|
-
if (!bound)
|
|
77
|
-
return undefined;
|
|
78
|
-
const settled = settlementDocument(bound, undefined);
|
|
79
|
-
settled.state = "done";
|
|
80
|
-
delete settled.startedAt;
|
|
81
|
-
return settled;
|
|
82
|
-
}
|