@astrosheep/keiyaku 4.6.3 → 4.6.6
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/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +47 -11
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/references/automation.md +2 -2
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-babysit/SKILL.md +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +8 -9
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +37 -25
- package/build/src/akuma/akuma-observe.d.ts +18 -15
- package/build/src/akuma/akuma-observe.js +75 -16
- package/build/src/akuma/akuma-wait.d.ts +4 -3
- package/build/src/akuma/akuma-wait.js +75 -53
- package/build/src/akuma/akuma.d.ts +6 -4
- package/build/src/akuma/akuma.js +4 -0
- package/build/src/akuma/call-initial-tell.d.ts +2 -2
- package/build/src/akuma/call-initial-tell.js +1 -1
- package/build/src/akuma/call-request.d.ts +2 -2
- package/build/src/akuma/heart/index.d.ts +7 -2
- package/build/src/akuma/heart/index.js +50 -5
- package/build/src/akuma/heart/tells.d.ts +33 -2
- package/build/src/akuma/heart/tells.js +24 -18
- package/build/src/akuma/heart/timeline.d.ts +23 -1
- package/build/src/akuma/heart/timeline.js +28 -21
- package/build/src/akuma/projection.d.ts +10 -10
- package/build/src/akuma/providers/pi/index.js +72 -39
- package/build/src/akuma/selection-observation.d.ts +20 -20
- package/build/src/akuma-body.js +1 -0
- package/build/src/alias/index.d.ts +0 -1
- package/build/src/alias/index.js +0 -4
- package/build/src/body/amend.d.ts +0 -4
- package/build/src/body/amend.js +0 -3
- package/build/src/body/region.d.ts +0 -1
- package/build/src/body/region.js +0 -3
- package/build/src/body/verification.d.ts +0 -1
- package/build/src/cli/commands/akuma-grammar.js +23 -66
- package/build/src/cli/commands/akuma.js +17 -30
- package/build/src/cli/commands/contract-grammar.d.ts +14 -1
- package/build/src/cli/commands/contract-grammar.js +54 -21
- package/build/src/cli/commands/contract-help.d.ts +9 -8
- package/build/src/cli/commands/contract-help.js +14 -7
- package/build/src/cli/commands/contract.js +57 -117
- package/build/src/cli/commands/install.d.ts +5 -5
- package/build/src/cli/commands/install.js +1 -4
- package/build/src/cli/commands/task-grammar.js +10 -66
- package/build/src/cli/commands/task.d.ts +0 -1
- package/build/src/cli/commands/task.js +34 -53
- package/build/src/cli/coordinates.d.ts +1 -2
- package/build/src/cli/coordinates.js +1 -4
- package/build/src/cli/parse.d.ts +2 -3
- package/build/src/cli/parse.js +24 -66
- package/build/src/cli/render/akuma.d.ts +3 -16
- package/build/src/cli/render/akuma.js +1 -13
- package/build/src/cli/render/audit.js +1 -1
- package/build/src/cli/render/contract.js +34 -32
- package/build/src/cli/render/kanshi-akuma.d.ts +8 -2
- package/build/src/cli/render/kanshi-akuma.js +14 -7
- package/build/src/cli/render/kanshi.d.ts +3 -1
- package/build/src/cli/render/kanshi.js +44 -32
- package/build/src/cli/render/receipt.js +6 -1
- package/build/src/cli/render/task.d.ts +1 -1
- package/build/src/cli/render/task.js +3 -27
- package/build/src/cli/render/terminal.d.ts +6 -2
- package/build/src/cli/render/terminal.js +8 -2
- package/build/src/cli/runtime.js +3 -16
- package/build/src/cli/scan.d.ts +26 -0
- package/build/src/cli/scan.js +82 -0
- package/build/src/cli/streams.d.ts +5 -0
- package/build/src/cli/streams.js +11 -0
- package/build/src/contract-worktree.js +38 -21
- package/build/src/core/facts/codec.d.ts +1 -2
- package/build/src/core/facts/codec.js +5 -11
- package/build/src/core/facts/types.d.ts +16 -2
- package/build/src/core/facts/types.js +15 -0
- package/build/src/core/verbs/abandon.js +3 -9
- package/build/src/core/verbs/amend.js +2 -9
- package/build/src/core/verbs/arc.js +6 -13
- package/build/src/core/verbs/attestation.js +2 -9
- package/build/src/core/verbs/bind.js +2 -9
- package/build/src/core/verbs/deliver.js +2 -9
- package/build/src/core/verbs/placement.d.ts +1 -1
- package/build/src/core/verbs/placement.js +2 -9
- package/build/src/core/verbs/reintegrate.d.ts +1 -1
- package/build/src/core/verbs/reintegrate.js +2 -9
- package/build/src/dispatch/index.d.ts +2 -2
- package/build/src/dispatch/index.js +33 -11
- package/build/src/git/hooks.d.ts +2 -4
- package/build/src/git/hooks.js +0 -12
- package/build/src/git/integration.d.ts +5 -3
- package/build/src/git/integration.js +36 -25
- package/build/src/git/nuke.js +4 -4
- package/build/src/git/observe.d.ts +4 -4
- package/build/src/git/observe.js +5 -5
- package/build/src/git/process.d.ts +1 -4
- package/build/src/git/process.js +1 -11
- package/build/src/git/read-observation.js +1 -1
- package/build/src/git/reconcile.d.ts +1 -1
- package/build/src/git/reconcile.js +15 -13
- package/build/src/git/repository.d.ts +2 -6
- package/build/src/git/repository.js +18 -31
- package/build/src/git/scratch.d.ts +6 -6
- package/build/src/git/scratch.js +110 -46
- package/build/src/git/target-placement.d.ts +5 -5
- package/build/src/git/target-placement.js +18 -33
- package/build/src/git/tender.d.ts +6 -2
- package/build/src/git/tender.js +19 -12
- package/build/src/git/terminal-reconcile.js +24 -13
- package/build/src/git/terminal-seal.d.ts +2 -5
- package/build/src/git/terminal-seal.js +5 -3
- package/build/src/git/workspace.d.ts +17 -13
- package/build/src/git/workspace.js +58 -41
- package/build/src/kanshi/index.d.ts +2 -2
- package/build/src/kanshi/index.js +1 -1
- package/build/src/kanshi/read.d.ts +8 -1
- package/build/src/kanshi/read.js +72 -7
- package/build/src/kanshi/report.d.ts +12 -0
- package/build/src/library/akuma-creation.d.ts +4 -4
- package/build/src/library/bind.d.ts +1 -63
- package/build/src/library/bind.js +3 -9
- package/build/src/library/contract-creation.js +2 -30
- package/build/src/library/contract-execution.d.ts +0 -1
- package/build/src/library/contract-execution.js +9 -34
- package/build/src/library/contract-operations.d.ts +5 -1
- package/build/src/library/contract-operations.js +7 -3
- package/build/src/library/contract-types.d.ts +1 -1
- package/build/src/library/delivery.d.ts +91 -93
- package/build/src/library/delivery.js +2 -1
- package/build/src/library/input.d.ts +0 -2
- package/build/src/library/input.js +0 -4
- package/build/src/library/invocation.d.ts +0 -1
- package/build/src/library/invocation.js +3 -25
- package/build/src/library/outcome.d.ts +651 -602
- package/build/src/library/outcome.js +4 -4
- package/build/src/library/reconcile.d.ts +6 -0
- package/build/src/library/reconcile.js +31 -1
- package/build/src/library/repo.d.ts +0 -1
- package/build/src/library/selection.d.ts +1 -10
- package/build/src/library/selection.js +14 -15
- package/build/src/protocol/abandon.d.ts +1 -1
- package/build/src/protocol/abandon.js +3 -10
- package/build/src/protocol/amend.js +2 -5
- package/build/src/protocol/arc.d.ts +1 -1
- package/build/src/protocol/arc.js +2 -9
- package/build/src/protocol/attempt.d.ts +3 -20
- package/build/src/protocol/attempt.js +6 -5
- package/build/src/protocol/audit.d.ts +1 -0
- package/build/src/protocol/audit.js +10 -11
- package/build/src/protocol/completion.d.ts +129 -55
- package/build/src/protocol/completion.js +62 -73
- package/build/src/protocol/deliver.d.ts +7 -5
- package/build/src/protocol/deliver.js +74 -44
- package/build/src/protocol/execution-observation.d.ts +2 -4
- package/build/src/protocol/execution-observation.js +3 -7
- package/build/src/protocol/intent.d.ts +1 -1
- package/build/src/protocol/intent.js +1 -0
- package/build/src/protocol/operations.d.ts +24 -12
- package/build/src/protocol/operations.js +36 -15
- package/build/src/protocol/placement.d.ts +2 -1
- package/build/src/protocol/placement.js +21 -5
- package/build/src/protocol/progress.d.ts +1 -1
- package/build/src/protocol/read/status.js +58 -41
- package/build/src/protocol/reintegrate.d.ts +1 -1
- package/build/src/protocol/reintegrate.js +10 -19
- package/build/src/protocol/review.d.ts +3 -28
- package/build/src/protocol/review.js +10 -37
- package/build/src/protocol/run.d.ts +19 -7
- package/build/src/protocol/run.js +19 -9
- package/build/src/runtime/proc/windows-launch.exe +0 -0
- package/build/src/settlement/holder.d.ts +2 -4
- package/build/src/settlement/holder.js +3 -6
- package/build/src/task/board.d.ts +6 -0
- package/build/src/task/board.js +30 -1
- package/build/src/task/catalog.js +1 -4
- package/build/src/task/compose.d.ts +0 -1
- package/build/src/task/compose.js +0 -1
- package/build/src/task/context.d.ts +0 -1
- package/build/src/task/context.js +0 -3
- package/build/src/task/created-observation.d.ts +5 -1
- package/build/src/task/created-observation.js +12 -5
- package/build/src/task/index.d.ts +1 -1
- package/build/src/task/index.js +7 -6
- package/build/src/task/input.d.ts +0 -6
- package/build/src/task/input.js +2 -14
- package/build/src/task/mutation.d.ts +3 -4
- package/build/src/task/mutation.js +11 -39
- package/build/src/task/operations.d.ts +2 -7
- package/build/src/task/operations.js +37 -49
- package/build/src/task/query.d.ts +0 -1
- package/build/src/task/query.js +1 -4
- package/build/src/task/store.d.ts +0 -2
- package/build/src/task/store.js +27 -11
- package/build/src/verification/declaration.js +3 -2
- package/build/src/verification/execution.d.ts +3 -2
- package/build/src/verification/execution.js +10 -6
- package/build/src/workspace-place.d.ts +0 -1
- package/build/src/workspace-place.js +0 -11
- package/package.json +7 -3
package/build/src/git/tender.js
CHANGED
|
@@ -3,7 +3,7 @@ import { z } from "zod";
|
|
|
3
3
|
import { access } from "node:fs/promises";
|
|
4
4
|
import { gitObjectIdForSnapshot, mintSnapshotId } from "./identity.js";
|
|
5
5
|
import { decodeGitNumstat, registeredWorktreePaths } from "./repository.js";
|
|
6
|
-
import {
|
|
6
|
+
import { executeGit, GitPlumbingError } from "./process.js";
|
|
7
7
|
import { captureWorkspaceTree, unmergedWorkspacePaths, workspaceMergeHead, worktreePath } from "./workspace.js";
|
|
8
8
|
export const worktreeMissingRefusalSchema = z
|
|
9
9
|
.object({ kind: z.literal("worktree-missing"), contractId: contractIdSchema })
|
|
@@ -69,7 +69,7 @@ export async function captureTender(repository, input) {
|
|
|
69
69
|
};
|
|
70
70
|
}
|
|
71
71
|
async function dirtyShortStat(repository, tender) {
|
|
72
|
-
return decodeGitNumstat(await
|
|
72
|
+
return decodeGitNumstat(await executeGit(repository, ["diff", "--numstat", "-z", gitObjectIdForSnapshot(tender.head), tender.tree]));
|
|
73
73
|
}
|
|
74
74
|
export async function dirtyTenderRefusal(repository, contractId, tender) {
|
|
75
75
|
return {
|
|
@@ -87,7 +87,7 @@ export async function dirtyTenderDelta(repository, tender) {
|
|
|
87
87
|
}
|
|
88
88
|
async function effectiveConfig(repository, key) {
|
|
89
89
|
try {
|
|
90
|
-
const value = (await
|
|
90
|
+
const value = (await executeGit(repository, ["config", "--get", key])).toString("utf8").trim();
|
|
91
91
|
return value.length === 0 ? undefined : value;
|
|
92
92
|
}
|
|
93
93
|
catch (error) {
|
|
@@ -119,22 +119,29 @@ export async function prepareDeliveryCommitMetadata(repository, input) {
|
|
|
119
119
|
identity,
|
|
120
120
|
};
|
|
121
121
|
}
|
|
122
|
-
/**
|
|
123
|
-
export
|
|
124
|
-
|
|
125
|
-
return tender.head;
|
|
126
|
-
const parents = ["-p", gitObjectIdForSnapshot(tender.head)];
|
|
127
|
-
if (tender.mergeHead !== undefined)
|
|
128
|
-
parents.push("-p", gitObjectIdForSnapshot(tender.mergeHead));
|
|
129
|
-
const oid = (await runGitWithEnvironment(repository, ["commit-tree", tender.tree, ...parents], commit.message, {
|
|
122
|
+
/** The Git identity environment for one delivery snapshot commit. */
|
|
123
|
+
export function deliveryEnvironment(commit) {
|
|
124
|
+
return {
|
|
130
125
|
GIT_AUTHOR_NAME: commit.identity.name,
|
|
131
126
|
GIT_AUTHOR_EMAIL: commit.identity.email,
|
|
132
127
|
GIT_COMMITTER_NAME: commit.identity.name,
|
|
133
128
|
GIT_COMMITTER_EMAIL: commit.identity.email,
|
|
134
129
|
GIT_AUTHOR_DATE: commit.at,
|
|
135
130
|
GIT_COMMITTER_DATE: commit.at,
|
|
136
|
-
}
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
/** Materialize one delivery snapshot commit over the given parents. */
|
|
134
|
+
export async function materializeSnapshotCommit(repository, tree, parents, message, environment) {
|
|
135
|
+
const args = parents.flatMap((parent) => ["-p", gitObjectIdForSnapshot(parent)]);
|
|
136
|
+
const oid = (await executeGit(repository, ["commit-tree", tree, ...args], message, environment))
|
|
137
137
|
.toString("utf8")
|
|
138
138
|
.trim();
|
|
139
139
|
return mintSnapshotId(oid);
|
|
140
140
|
}
|
|
141
|
+
/** Materialize the tender tree when it contains admitted dirty workspace bytes. */
|
|
142
|
+
export async function materializeTenderSnapshot(repository, tender, commit) {
|
|
143
|
+
if (!tender.dirty && tender.mergeHead === undefined)
|
|
144
|
+
return tender.head;
|
|
145
|
+
const parents = tender.mergeHead === undefined ? [tender.head] : [tender.head, tender.mergeHead];
|
|
146
|
+
return await materializeSnapshotCommit(repository, tender.tree, parents, commit.message, deliveryEnvironment(commit));
|
|
147
|
+
}
|
|
@@ -3,7 +3,7 @@ import { isKeiyakuOwnedRef, readRef, writeCommit } from "./repository.js";
|
|
|
3
3
|
import { gitObjectIdForSnapshot, mintSnapshotId } from "./identity.js";
|
|
4
4
|
import { runDestroyHooks } from "./hooks.js";
|
|
5
5
|
import { terminalSealExpectations as decodeTerminalSealExpectations, terminalSealSnapshots, observeTerminalWorkspace, } from "./terminal-seal.js";
|
|
6
|
-
import { GitPlumbingError,
|
|
6
|
+
import { GitPlumbingError, executeGit } from "./process.js";
|
|
7
7
|
import { worktreePath } from "./workspace.js";
|
|
8
8
|
async function pathExists(path) {
|
|
9
9
|
try {
|
|
@@ -16,29 +16,38 @@ async function pathExists(path) {
|
|
|
16
16
|
throw error;
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
|
+
/** Attempt removal once. Retention is a lag carrying its reason; no effect claims the worktree state after it. */
|
|
19
20
|
async function removeTerminalWorktree(repository, topology, path) {
|
|
20
21
|
const registered = topology.paths.has(path);
|
|
21
22
|
if (!registered) {
|
|
22
|
-
return
|
|
23
|
+
return (await pathExists(path)) ? { retainedLag: { kind: "worktree-retained", path } } : {};
|
|
23
24
|
}
|
|
24
25
|
const present = await pathExists(path);
|
|
25
26
|
try {
|
|
26
|
-
await
|
|
27
|
+
await executeGit(repository, ["worktree", "remove", ...(present ? ["--force"] : []), path]);
|
|
27
28
|
}
|
|
28
29
|
catch (error) {
|
|
29
30
|
if (!(error instanceof GitPlumbingError))
|
|
30
31
|
throw error;
|
|
31
|
-
return {
|
|
32
|
+
return { retainedLag: { kind: "worktree-retained", path, diagnostic: error.message } };
|
|
32
33
|
}
|
|
33
34
|
topology.paths.delete(path);
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
if (await pathExists(path)) {
|
|
36
|
+
return {
|
|
37
|
+
retainedLag: {
|
|
38
|
+
kind: "worktree-retained",
|
|
39
|
+
path,
|
|
40
|
+
diagnostic: `Git reported worktree removal but the path remains: ${path}`,
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
return { effect: { kind: "worktree", path, action: "removed" } };
|
|
36
45
|
}
|
|
37
46
|
export async function updateRef(repository, ref, desired) {
|
|
38
47
|
const before = await readRef(repository, ref);
|
|
39
48
|
if (before === desired)
|
|
40
49
|
return { kind: "ref", name: ref, action: "unchanged", before, after: desired };
|
|
41
|
-
await
|
|
50
|
+
await executeGit(repository, [
|
|
42
51
|
"update-ref",
|
|
43
52
|
"--no-deref",
|
|
44
53
|
ref,
|
|
@@ -51,7 +60,7 @@ export async function removeRef(repository, ref) {
|
|
|
51
60
|
const before = await readRef(repository, ref);
|
|
52
61
|
if (before === null)
|
|
53
62
|
return { kind: "ref", name: ref, action: "unchanged", before: null, after: null };
|
|
54
|
-
await
|
|
63
|
+
await executeGit(repository, ["update-ref", "--no-deref", "-d", ref, before]);
|
|
55
64
|
return { kind: "ref", name: ref, action: "removed", before, after: null };
|
|
56
65
|
}
|
|
57
66
|
async function removeRefWithCustody(repository, ref, custodian, expectedCustodian) {
|
|
@@ -61,7 +70,7 @@ async function removeRefWithCustody(repository, ref, custodian, expectedCustodia
|
|
|
61
70
|
if ((await readRef(repository, custodian)) !== expectedCustodian) {
|
|
62
71
|
return { kind: "ref", name: ref, action: "unchanged", before, after: before };
|
|
63
72
|
}
|
|
64
|
-
await
|
|
73
|
+
await executeGit(repository, ["update-ref", "--stdin", "--no-deref"], [
|
|
65
74
|
"start",
|
|
66
75
|
`verify ${custodian} ${gitObjectIdForSnapshot(expectedCustodian)}`,
|
|
67
76
|
`delete ${ref} ${before}`,
|
|
@@ -75,7 +84,7 @@ function retainedRefEffect(ref, snapshot) {
|
|
|
75
84
|
return { kind: "ref", name: ref, action: "unchanged", before: snapshot, after: snapshot };
|
|
76
85
|
}
|
|
77
86
|
async function refRows(repository) {
|
|
78
|
-
const output = (await
|
|
87
|
+
const output = (await executeGit(repository, ["for-each-ref", "--format=%(refname)%00%(objectname)"])).toString("utf8");
|
|
79
88
|
if (output.length === 0)
|
|
80
89
|
return [];
|
|
81
90
|
return output
|
|
@@ -90,7 +99,7 @@ async function refRows(repository) {
|
|
|
90
99
|
}
|
|
91
100
|
async function commitIsAncestor(repository, ancestor, descendant) {
|
|
92
101
|
try {
|
|
93
|
-
await
|
|
102
|
+
await executeGit(repository, ["merge-base", "--is-ancestor", gitObjectIdForSnapshot(ancestor), descendant]);
|
|
94
103
|
return true;
|
|
95
104
|
}
|
|
96
105
|
catch (error) {
|
|
@@ -181,8 +190,10 @@ async function removeSealedTerminalWorktree({ repository, topology, path, state,
|
|
|
181
190
|
}
|
|
182
191
|
}
|
|
183
192
|
const removal = await removeTerminalWorktree(repository, topology, path);
|
|
184
|
-
if (removal.
|
|
185
|
-
|
|
193
|
+
if (removal.retainedLag !== undefined) {
|
|
194
|
+
acc.recordLag(removal.retainedLag);
|
|
195
|
+
return complete(acc.effects, acc.lag, acc.hookRuns);
|
|
196
|
+
}
|
|
186
197
|
if (removal.effect !== undefined)
|
|
187
198
|
acc.recordEffect(removal.effect);
|
|
188
199
|
return null;
|
|
@@ -3,7 +3,6 @@ import type { ContractState, SnapshotId } from "../core/facts/types.js";
|
|
|
3
3
|
import { type GitObjectId } from "./identity.js";
|
|
4
4
|
import type { GitOid } from "./repository.js";
|
|
5
5
|
import { type GitRepository } from "./process.js";
|
|
6
|
-
import { captureWorkspaceTree } from "./workspace.js";
|
|
7
6
|
export declare const unsealedBytesSchema: z.ZodObject<{
|
|
8
7
|
kind: z.ZodLiteral<"unsealed-bytes">;
|
|
9
8
|
path: z.ZodString;
|
|
@@ -16,25 +15,23 @@ export type TerminalSealExpectations = Readonly<{
|
|
|
16
15
|
trees: readonly GitObjectId[];
|
|
17
16
|
treeBySnapshot: ReadonlyMap<SnapshotId, GitObjectId>;
|
|
18
17
|
}>;
|
|
19
|
-
|
|
18
|
+
type TerminalSealObject = Readonly<{
|
|
20
19
|
kind: "present";
|
|
21
20
|
type: string;
|
|
22
21
|
bytes: Buffer;
|
|
23
22
|
}> | Readonly<{
|
|
24
23
|
kind: "missing";
|
|
25
24
|
}>;
|
|
26
|
-
type WorkspaceTree = Awaited<ReturnType<typeof captureWorkspaceTree>>;
|
|
27
25
|
export type TerminalWorkspace = Readonly<{
|
|
28
26
|
tree: GitObjectId;
|
|
29
27
|
head: SnapshotId;
|
|
30
28
|
submodules: readonly string[];
|
|
31
29
|
}>;
|
|
32
|
-
|
|
30
|
+
type TerminalWorkspaceObservation = Readonly<{
|
|
33
31
|
workspace: TerminalWorkspace;
|
|
34
32
|
unsealed: UnsealedBytes | null;
|
|
35
33
|
}>;
|
|
36
34
|
export declare function terminalSealSnapshots(state: ContractState): readonly SnapshotId[];
|
|
37
35
|
export declare function terminalSealExpectations(state: ContractState, objects: ReadonlyMap<GitOid, TerminalSealObject>): TerminalSealExpectations;
|
|
38
|
-
export declare function unsealedWorkspaceBytes(repository: GitRepository, path: string, expected: TerminalSealExpectations, workspace: WorkspaceTree): Promise<UnsealedBytes | null>;
|
|
39
36
|
export declare function observeTerminalWorkspace(repository: GitRepository, path: string, expected: TerminalSealExpectations): Promise<TerminalWorkspaceObservation>;
|
|
40
37
|
export {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { mintedSnapshotInputSchema } from "./identity.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { gitObjectId, gitObjectIdForSnapshot, mintSnapshotId } from "./identity.js";
|
|
4
|
-
import {
|
|
4
|
+
import { executeGit } from "./process.js";
|
|
5
5
|
import { captureWorkspaceTree } from "./workspace.js";
|
|
6
6
|
export const unsealedBytesSchema = z
|
|
7
7
|
.object({
|
|
@@ -71,12 +71,14 @@ export function terminalSealExpectations(state, objects) {
|
|
|
71
71
|
};
|
|
72
72
|
}
|
|
73
73
|
async function changedPaths(repository, left, right) {
|
|
74
|
-
const fields = (await
|
|
74
|
+
const fields = (await executeGit(repository, ["diff", "--name-only", "-z", left, right]))
|
|
75
|
+
.toString("utf8")
|
|
76
|
+
.split("\0");
|
|
75
77
|
if (fields.at(-1) !== "")
|
|
76
78
|
throw new Error("Git tree diff paths are not NUL terminated");
|
|
77
79
|
return fields.slice(0, -1).sort();
|
|
78
80
|
}
|
|
79
|
-
|
|
81
|
+
async function unsealedWorkspaceBytes(repository, path, expected, workspace) {
|
|
80
82
|
const headIsSealed = expected.heads.includes(workspace.head);
|
|
81
83
|
if (workspace.changes.submodules.length > 0) {
|
|
82
84
|
return {
|
|
@@ -2,8 +2,9 @@ import { z } from "zod";
|
|
|
2
2
|
import type { ContractId, SnapshotId } from "../core/facts/types.js";
|
|
3
3
|
import { type GitObjectId } from "./identity.js";
|
|
4
4
|
import { type GitRepository } from "./process.js";
|
|
5
|
+
import type { GitReadObservation } from "./read-observation.js";
|
|
5
6
|
import type { ContractWorkspaceLocation } from "../workspace-place.js";
|
|
6
|
-
|
|
7
|
+
type WorkspaceChanges = Readonly<{
|
|
7
8
|
staged: readonly string[];
|
|
8
9
|
unstaged: readonly string[];
|
|
9
10
|
untracked: readonly string[];
|
|
@@ -19,7 +20,7 @@ export type WorkspaceTree = Readonly<{
|
|
|
19
20
|
export declare function worktreePath(repository: GitRepository, place: string): string;
|
|
20
21
|
/** Check out a captured candidate tree in the appointed detached worktree. */
|
|
21
22
|
export declare function checkoutDetachedSnapshot(repository: GitRepository, workspace: string, snapshot: SnapshotId): Promise<void>;
|
|
22
|
-
|
|
23
|
+
type DependentWorktreeFollow = Readonly<{
|
|
23
24
|
kind: "followed";
|
|
24
25
|
before: SnapshotId;
|
|
25
26
|
after: SnapshotId;
|
|
@@ -42,7 +43,7 @@ export declare function workspaceMergeHead(repository: GitRepository, workspace:
|
|
|
42
43
|
export declare function workspaceMergeStatePresent(repository: GitRepository, workspace: string): Promise<boolean>;
|
|
43
44
|
/** Sorted unique unmerged index paths from porcelain status. */
|
|
44
45
|
export declare function unmergedWorkspacePaths(repository: GitRepository, workspace: string): Promise<readonly string[]>;
|
|
45
|
-
|
|
46
|
+
type WorkspaceChangeCounts = Readonly<{
|
|
46
47
|
staged: number;
|
|
47
48
|
unstaged: number;
|
|
48
49
|
untracked: number;
|
|
@@ -55,7 +56,7 @@ export declare const conflictRecoverySchema: z.ZodObject<{
|
|
|
55
56
|
}, z.core.$strict>;
|
|
56
57
|
type ConflictRecovery = z.infer<typeof conflictRecoverySchema>;
|
|
57
58
|
export declare const conflictRecovery: ConflictRecovery;
|
|
58
|
-
|
|
59
|
+
type ContractWorkspaceMerge = Readonly<{
|
|
59
60
|
head: SnapshotId;
|
|
60
61
|
unmergedPaths: readonly string[];
|
|
61
62
|
handoffBase?: SnapshotId;
|
|
@@ -100,7 +101,16 @@ export declare function observeWorkspace(repository: GitRepository, location: Co
|
|
|
100
101
|
export declare function observeTargetLag(repository: GitRepository, workspace: string, head: SnapshotId | null | undefined): Promise<ContractTargetLag>;
|
|
101
102
|
/** Capture tracked and untracked workspace bytes without changing its real index. */
|
|
102
103
|
export declare function captureWorkspaceTree(repository: GitRepository, workspace: string): Promise<WorkspaceTree>;
|
|
103
|
-
export type
|
|
104
|
+
export type HandoffReceiptCoordinate = Readonly<{
|
|
105
|
+
contractId: ContractId;
|
|
106
|
+
place: string;
|
|
107
|
+
workspace: string;
|
|
108
|
+
}>;
|
|
109
|
+
export type ConflictHandoff = Readonly<{
|
|
110
|
+
base: SnapshotId;
|
|
111
|
+
mergeHead: SnapshotId;
|
|
112
|
+
}>;
|
|
113
|
+
type HandoffRetirement = Readonly<{
|
|
104
114
|
kind: "absent" | "active" | "orphan-retired" | "retired";
|
|
105
115
|
}> | Readonly<{
|
|
106
116
|
kind: "retained";
|
|
@@ -113,14 +123,8 @@ export declare function recordConflictHandoff(repository: GitRepository, input:
|
|
|
113
123
|
head: SnapshotId;
|
|
114
124
|
mergeHead: SnapshotId;
|
|
115
125
|
}>): Promise<void>;
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
place: string;
|
|
119
|
-
workspace: string;
|
|
120
|
-
}>): Promise<Readonly<{
|
|
121
|
-
base: SnapshotId;
|
|
122
|
-
mergeHead: SnapshotId;
|
|
123
|
-
}> | undefined>;
|
|
126
|
+
/** Read selected handoff refs once and decode their receipts through the call's blob channel. */
|
|
127
|
+
export declare function readConflictHandoffs(repository: GitRepository, readBlobs: GitReadObservation["readBlobs"], coordinates: readonly HandoffReceiptCoordinate[]): Promise<ReadonlyMap<ContractId, ConflictHandoff>>;
|
|
124
128
|
export declare function retireConflictHandoff(repository: GitRepository, input: Readonly<{
|
|
125
129
|
contractId: ContractId;
|
|
126
130
|
place: string;
|
|
@@ -2,20 +2,21 @@ import { z } from "zod";
|
|
|
2
2
|
import { access, mkdtemp, rm } from "node:fs/promises";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join, resolve } from "node:path";
|
|
5
|
+
import { AuthorityCorruptionError } from "../core/facts/errors.js";
|
|
5
6
|
import { contractPhysicalName, gitObjectId, mintSnapshotId } from "./identity.js";
|
|
6
|
-
import {
|
|
7
|
-
import { HANDOFF_RECEIPT_REF_NAMESPACE, readBlob, readRef, worktreeGitDirectory, writeBlob } from "./repository.js";
|
|
7
|
+
import { executeGit, GitPlumbingError } from "./process.js";
|
|
8
|
+
import { HANDOFF_RECEIPT_REF_NAMESPACE, readBlob, readRef, readRefs, worktreeGitDirectory, writeBlob, } from "./repository.js";
|
|
8
9
|
const WORKTREE_DIRECTORY = [".keiyaku", "wt"];
|
|
9
10
|
export function worktreePath(repository, place) {
|
|
10
11
|
return resolve(repository.primaryWorktree, ...WORKTREE_DIRECTORY, place);
|
|
11
12
|
}
|
|
12
13
|
/** Check out a captured candidate tree in the appointed detached worktree. */
|
|
13
14
|
export async function checkoutDetachedSnapshot(repository, workspace, snapshot) {
|
|
14
|
-
await
|
|
15
|
+
await executeGit(repository, ["-C", workspace, "checkout", "--quiet", "--force", "--detach", gitObjectId(snapshot)]);
|
|
15
16
|
}
|
|
16
17
|
async function workspaceRevision(repository, workspace, revision) {
|
|
17
18
|
try {
|
|
18
|
-
const value = (await
|
|
19
|
+
const value = (await executeGit(repository, ["-C", workspace, "rev-parse", "--verify", "--quiet", revision]))
|
|
19
20
|
.toString("utf8")
|
|
20
21
|
.trim();
|
|
21
22
|
return value.length === 0 ? null : mintSnapshotId(value);
|
|
@@ -37,7 +38,7 @@ async function workspaceOperationState(repository, workspace, gitDirectory) {
|
|
|
37
38
|
return false;
|
|
38
39
|
}
|
|
39
40
|
}));
|
|
40
|
-
const unmerged = (await
|
|
41
|
+
const unmerged = (await executeGit(repository, ["-C", workspace, "ls-files", "--unmerged", "-z"])).length > 0;
|
|
41
42
|
return { other: other.some(Boolean), unmerged };
|
|
42
43
|
}
|
|
43
44
|
/** Follow a dependency baseline only through a clean, detached fast-forward. */
|
|
@@ -58,7 +59,7 @@ export async function followDependentManagedWorktree(repository, workspace, targ
|
|
|
58
59
|
...status.unmergedPaths,
|
|
59
60
|
]),
|
|
60
61
|
].sort();
|
|
61
|
-
const attached = await
|
|
62
|
+
const attached = await executeGit(repository, ["-C", workspace, "symbolic-ref", "--quiet", "HEAD"]).then(() => true, (error) => {
|
|
62
63
|
if (error instanceof GitPlumbingError && error.status === 1)
|
|
63
64
|
return false;
|
|
64
65
|
throw error;
|
|
@@ -72,7 +73,7 @@ export async function followDependentManagedWorktree(repository, workspace, targ
|
|
|
72
73
|
return { kind: "unchanged" };
|
|
73
74
|
let ancestor = false;
|
|
74
75
|
try {
|
|
75
|
-
await
|
|
76
|
+
await executeGit(repository, ["merge-base", "--is-ancestor", gitObjectId(head), gitObjectId(target)]);
|
|
76
77
|
ancestor = true;
|
|
77
78
|
}
|
|
78
79
|
catch (error) {
|
|
@@ -81,17 +82,26 @@ export async function followDependentManagedWorktree(repository, workspace, targ
|
|
|
81
82
|
}
|
|
82
83
|
if (!ancestor)
|
|
83
84
|
return { kind: "retained", head, reason: "head-moved", paths };
|
|
84
|
-
await
|
|
85
|
+
await executeGit(repository, ["-C", workspace, "checkout", "--quiet", "--detach", gitObjectId(target)]);
|
|
85
86
|
return { kind: "followed", before: head, after: target };
|
|
86
87
|
}
|
|
87
88
|
export async function withPrivateGitIndex(action) {
|
|
88
89
|
const directory = await mkdtemp(join(tmpdir(), "keiyaku-v4-index-"));
|
|
90
|
+
let value;
|
|
89
91
|
try {
|
|
90
|
-
|
|
92
|
+
value = await action({ GIT_INDEX_FILE: join(directory, "index") });
|
|
91
93
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
+
catch (error) {
|
|
95
|
+
try {
|
|
96
|
+
await rm(directory, { recursive: true, force: true });
|
|
97
|
+
}
|
|
98
|
+
catch (cleanup) {
|
|
99
|
+
throw new AggregateError([error, cleanup], "Private Git index capture and cleanup failed");
|
|
100
|
+
}
|
|
101
|
+
throw error;
|
|
94
102
|
}
|
|
103
|
+
await rm(directory, { recursive: true, force: true });
|
|
104
|
+
return value;
|
|
95
105
|
}
|
|
96
106
|
function fields(record, count) {
|
|
97
107
|
const result = [];
|
|
@@ -107,15 +117,7 @@ function fields(record, count) {
|
|
|
107
117
|
return result;
|
|
108
118
|
}
|
|
109
119
|
async function workspaceStatus(repository, workspace) {
|
|
110
|
-
const records = (await
|
|
111
|
-
"-C",
|
|
112
|
-
workspace,
|
|
113
|
-
"status",
|
|
114
|
-
"--porcelain=v2",
|
|
115
|
-
"-z",
|
|
116
|
-
"--untracked-files=all",
|
|
117
|
-
"--ignore-submodules=none",
|
|
118
|
-
]))
|
|
120
|
+
const records = (await executeGit(repository, ["-C", workspace, "status", "--porcelain=v2", "-z", "--untracked-files=all", "--ignore-submodules=none"], undefined, { GIT_OPTIONAL_LOCKS: "0" }))
|
|
119
121
|
.toString("utf8")
|
|
120
122
|
.split("\0");
|
|
121
123
|
const staged = new Set();
|
|
@@ -172,7 +174,7 @@ async function workspaceChanges(repository, workspace) {
|
|
|
172
174
|
/** Observe MERGE_HEAD in an appointed workspace, or undefined when absent. */
|
|
173
175
|
export async function workspaceMergeHead(repository, workspace) {
|
|
174
176
|
try {
|
|
175
|
-
return mintSnapshotId((await
|
|
177
|
+
return mintSnapshotId((await executeGit(repository, ["-C", workspace, "rev-parse", "-q", "--verify", "MERGE_HEAD"]))
|
|
176
178
|
.toString("utf8")
|
|
177
179
|
.trim());
|
|
178
180
|
}
|
|
@@ -250,7 +252,7 @@ export async function observeTargetLag(repository, workspace, head) {
|
|
|
250
252
|
if (head === null)
|
|
251
253
|
return { kind: "unknown", subject };
|
|
252
254
|
try {
|
|
253
|
-
const text = (await
|
|
255
|
+
const text = (await executeGit(repository, ["-C", workspace, "rev-list", "--count", `HEAD..${head}`]))
|
|
254
256
|
.toString("utf8")
|
|
255
257
|
.trim();
|
|
256
258
|
if (!/^[0-9]+$/u.test(text))
|
|
@@ -265,7 +267,7 @@ export async function observeTargetLag(repository, workspace, head) {
|
|
|
265
267
|
}
|
|
266
268
|
/** Capture tracked and untracked workspace bytes without changing its real index. */
|
|
267
269
|
export async function captureWorkspaceTree(repository, workspace) {
|
|
268
|
-
const identities = (await
|
|
270
|
+
const identities = (await executeGit(repository, ["-C", workspace, "show", "-s", "--format=%H%n%T%n%cI", "HEAD"]))
|
|
269
271
|
.toString("utf8")
|
|
270
272
|
.split("\n");
|
|
271
273
|
if (identities.length !== 4 ||
|
|
@@ -285,10 +287,10 @@ export async function captureWorkspaceTree(repository, workspace) {
|
|
|
285
287
|
if (!dirty)
|
|
286
288
|
return { tree, head, at, dirty, changes };
|
|
287
289
|
return await withPrivateGitIndex(async (environment) => {
|
|
288
|
-
await
|
|
289
|
-
await
|
|
290
|
+
await executeGit(repository, ["-C", workspace, "-c", "core.splitIndex=false", "read-tree", gitObjectId(head)], undefined, environment);
|
|
291
|
+
await executeGit(repository, ["-C", workspace, "-c", "core.splitIndex=false", "add", "--all"], undefined, environment);
|
|
290
292
|
return {
|
|
291
|
-
tree: gitObjectId((await
|
|
293
|
+
tree: gitObjectId((await executeGit(repository, ["-C", workspace, "-c", "core.splitIndex=false", "write-tree"], undefined, environment))
|
|
292
294
|
.toString("utf8")
|
|
293
295
|
.trim(), "workspace tree"),
|
|
294
296
|
head,
|
|
@@ -320,6 +322,11 @@ function receiptFor(value) {
|
|
|
320
322
|
mergeHead: mintSnapshotId(record.mergeHead),
|
|
321
323
|
};
|
|
322
324
|
}
|
|
325
|
+
function receiptMatches(receipt, coordinate) {
|
|
326
|
+
return (receipt.contractId === coordinate.contractId &&
|
|
327
|
+
receipt.place === coordinate.place &&
|
|
328
|
+
receipt.workspace === resolve(coordinate.workspace));
|
|
329
|
+
}
|
|
323
330
|
async function storedHandoffReceipt(repository, contractId, place) {
|
|
324
331
|
const ref = handoffRefFor(contractId, place);
|
|
325
332
|
const oid = await readRef(repository, ref);
|
|
@@ -329,7 +336,7 @@ async function storedHandoffReceipt(repository, contractId, place) {
|
|
|
329
336
|
return { receipt, ref, oid };
|
|
330
337
|
}
|
|
331
338
|
async function removeHandoffReceipt(repository, stored) {
|
|
332
|
-
await
|
|
339
|
+
await executeGit(repository, ["update-ref", "--no-deref", "-d", stored.ref, stored.oid]);
|
|
333
340
|
}
|
|
334
341
|
export async function recordConflictHandoff(repository, input) {
|
|
335
342
|
const ref = handoffRefFor(input.contractId, input.place);
|
|
@@ -345,27 +352,37 @@ export async function recordConflictHandoff(repository, input) {
|
|
|
345
352
|
mergeHead: input.mergeHead,
|
|
346
353
|
};
|
|
347
354
|
const oid = await writeBlob(repository, `${JSON.stringify(receipt)}\n`);
|
|
348
|
-
await
|
|
355
|
+
await executeGit(repository, ["update-ref", "--no-deref", ref, oid, "0".repeat(oid.length)]);
|
|
349
356
|
}
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
357
|
+
/** Read selected handoff refs once and decode their receipts through the call's blob channel. */
|
|
358
|
+
export async function readConflictHandoffs(repository, readBlobs, coordinates) {
|
|
359
|
+
const byRef = new Map();
|
|
360
|
+
for (const coordinate of coordinates)
|
|
361
|
+
byRef.set(handoffRefFor(coordinate.contractId, coordinate.place), coordinate);
|
|
362
|
+
if (byRef.size === 0)
|
|
363
|
+
return new Map();
|
|
364
|
+
const resolved = await readRefs(repository, [...byRef.keys()]);
|
|
365
|
+
const present = [...resolved].flatMap(([ref, oid]) => (oid === null ? [] : [{ ref, oid }]));
|
|
366
|
+
const blobs = await readBlobs(present.map(({ oid }) => oid));
|
|
367
|
+
const handoffs = new Map();
|
|
368
|
+
for (const { ref, oid } of present) {
|
|
369
|
+
const result = blobs.get(oid);
|
|
370
|
+
if (result?.kind !== "present")
|
|
371
|
+
throw new AuthorityCorruptionError(`Git handoff receipt is missing: ${ref}`);
|
|
372
|
+
const receipt = receiptFor(JSON.parse(result.bytes.toString("utf8")));
|
|
373
|
+
const coordinate = byRef.get(ref);
|
|
374
|
+
if (!receiptMatches(receipt, coordinate))
|
|
375
|
+
continue;
|
|
376
|
+
handoffs.set(coordinate.contractId, { base: receipt.head, mergeHead: receipt.mergeHead });
|
|
358
377
|
}
|
|
359
|
-
return
|
|
378
|
+
return handoffs;
|
|
360
379
|
}
|
|
361
380
|
export async function retireConflictHandoff(repository, input) {
|
|
362
381
|
const stored = await storedHandoffReceipt(repository, input.contractId, input.place);
|
|
363
382
|
if (stored === null)
|
|
364
383
|
return { kind: "absent" };
|
|
365
384
|
const { receipt } = stored;
|
|
366
|
-
if (receipt
|
|
367
|
-
receipt.place !== input.place ||
|
|
368
|
-
receipt.workspace !== resolve(input.workspace)) {
|
|
385
|
+
if (!receiptMatches(receipt, input)) {
|
|
369
386
|
return { kind: "retained", reason: "receipt-mismatch" };
|
|
370
387
|
}
|
|
371
388
|
const gitDirectory = await worktreeGitDirectory(repository, input.workspace);
|
|
@@ -389,7 +406,7 @@ export async function retireConflictHandoff(repository, input) {
|
|
|
389
406
|
}
|
|
390
407
|
if (!input.consume)
|
|
391
408
|
return { kind: "active" };
|
|
392
|
-
await
|
|
409
|
+
await executeGit(repository, ["-C", input.workspace, "merge", "--quit"]);
|
|
393
410
|
await removeHandoffReceipt(repository, stored);
|
|
394
411
|
return { kind: "retired" };
|
|
395
412
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { kanshi, observeKanshi } from "./read.js";
|
|
1
|
+
export { kanshi, kanshiCategory, observeKanshi } from "./read.js";
|
|
2
2
|
export { selectKanshi, selectRegion } from "./select.js";
|
|
3
3
|
export type { KanshiInput } from "./read.js";
|
|
4
|
-
export type { AkumaKanshiRow, AkumaKanshiWorld, ContractEndpointObservation, ContractHolderObservation, ContractKanshiBoard, ContractKanshiRow, KanshiReport, Section, TaskKanshiRow, TaskKanshiWorld, KanshiRegionSelection, RegionDeclaration, RegionOverlap, RegionRead, } from "./report.js";
|
|
4
|
+
export type { AkumaKanshiRow, AkumaKanshiWorld, ContractEndpointObservation, ContractHolderObservation, ContractKanshiBoard, ContractKanshiRow, KanshiCategoryReport, KanshiReport, Section, TaskKanshiRow, TaskKanshiWorld, KanshiRegionSelection, RegionDeclaration, RegionOverlap, RegionRead, } from "./report.js";
|
|
5
5
|
export type { KanshiSelection } from "./select.js";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { kanshi, observeKanshi } from "./read.js";
|
|
1
|
+
export { kanshi, kanshiCategory, observeKanshi } from "./read.js";
|
|
2
2
|
export { selectKanshi, selectRegion } from "./select.js";
|
|
@@ -2,7 +2,7 @@ import { type AliasBinding } from "../alias/index.js";
|
|
|
2
2
|
import { Repo } from "../library/repo.js";
|
|
3
3
|
import type { ContractBoard } from "../protocol/read/status.js";
|
|
4
4
|
import type { WorldRoot } from "../world.js";
|
|
5
|
-
import type { KanshiRegionSelection, KanshiReport, Section } from "./report.js";
|
|
5
|
+
import type { KanshiCategoryReport, KanshiRegionSelection, KanshiReport, Section } from "./report.js";
|
|
6
6
|
export type KanshiInput = Readonly<{
|
|
7
7
|
world: WorldRoot | null;
|
|
8
8
|
repo?: Repo;
|
|
@@ -15,3 +15,10 @@ export type KanshiObservation = Readonly<{
|
|
|
15
15
|
}>;
|
|
16
16
|
export declare function observeKanshi(input: KanshiInput): Promise<KanshiObservation>;
|
|
17
17
|
export declare function kanshi(input: KanshiInput): Promise<KanshiReport>;
|
|
18
|
+
export type KanshiCategoryInput = Readonly<{
|
|
19
|
+
world: WorldRoot | null;
|
|
20
|
+
repo?: Repo;
|
|
21
|
+
category: KanshiCategoryReport["category"];
|
|
22
|
+
limit?: number;
|
|
23
|
+
}>;
|
|
24
|
+
export declare function kanshiCategory(input: KanshiCategoryInput): Promise<KanshiCategoryReport>;
|