@astrosheep/keiyaku 4.6.4 → 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 +43 -8
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +7 -8
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +3 -3
- 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
|
@@ -3,13 +3,15 @@ import { z } from "zod";
|
|
|
3
3
|
import { AuthorityCorruptionError } from "../core/facts/errors.js";
|
|
4
4
|
import { gitObjectId, gitObjectIdForSnapshot, mintChangeId, mintSnapshotId } from "./identity.js";
|
|
5
5
|
import { decodeGitNameOnly, decodeGitNumstat, readRef } from "./repository.js";
|
|
6
|
-
import {
|
|
6
|
+
import { executeGit, GitPlumbingError, runGitPipe } from "./process.js";
|
|
7
|
+
import { deliveryEnvironment, materializeSnapshotCommit, } from "./tender.js";
|
|
7
8
|
import { workspaceMergeStatePresent } from "./workspace.js";
|
|
9
|
+
import { withGitDecodeChannel } from "./read-observation.js";
|
|
8
10
|
export { workspaceMergeStatePresent };
|
|
9
11
|
const REQUIRED_GIT = "2.38";
|
|
10
12
|
async function runAllowingNonzero(repository, args, input) {
|
|
11
13
|
try {
|
|
12
|
-
return { stdout: await
|
|
14
|
+
return { stdout: await executeGit(repository, args, input), stderr: Buffer.alloc(0), status: 0 };
|
|
13
15
|
}
|
|
14
16
|
catch (error) {
|
|
15
17
|
if (!(error instanceof GitPlumbingError))
|
|
@@ -76,22 +78,34 @@ export async function worktreeChangeId(repository, input, tender) {
|
|
|
76
78
|
const id = result.toString("utf8").trim().split(/\s/, 1)[0] ?? "";
|
|
77
79
|
return mintChangeId(id === "" ? "0000000000000000000000000000000000000000" : id);
|
|
78
80
|
}
|
|
81
|
+
/** Reuse only recorded commits with this exact tree and sole predecessor. */
|
|
82
|
+
export async function reusableIntegrationSnapshot(repository, tree, parent, recordedSnapshots) {
|
|
83
|
+
if (recordedSnapshots.length === 0)
|
|
84
|
+
return undefined;
|
|
85
|
+
return await withGitDecodeChannel(repository, async (channel) => {
|
|
86
|
+
for (const snapshot of recordedSnapshots) {
|
|
87
|
+
const oid = gitObjectIdForSnapshot(snapshot);
|
|
88
|
+
const object = (await channel.readObjects([oid])).get(oid);
|
|
89
|
+
if (object.kind === "missing")
|
|
90
|
+
continue;
|
|
91
|
+
if (object.type !== "commit") {
|
|
92
|
+
throw new AuthorityCorruptionError(`recorded integration snapshot is not a Git commit: ${snapshot}`);
|
|
93
|
+
}
|
|
94
|
+
const headers = object.bytes.toString("utf8").split("\n\n", 1)[0].split("\n");
|
|
95
|
+
const parents = headers.filter((line) => line.startsWith("parent "));
|
|
96
|
+
if (headers[0] === `tree ${tree}` && parents.length === 1 && parents[0] === `parent ${parent}`) {
|
|
97
|
+
return snapshot;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return undefined;
|
|
101
|
+
});
|
|
102
|
+
}
|
|
79
103
|
/** Materialize the exact integration tree against its observed predecessor. */
|
|
80
104
|
export async function materializeIntegrationSnapshot(repository, tree, parent, metadata) {
|
|
81
|
-
|
|
82
|
-
GIT_AUTHOR_NAME: metadata.identity.name,
|
|
83
|
-
GIT_AUTHOR_EMAIL: metadata.identity.email,
|
|
84
|
-
GIT_COMMITTER_NAME: metadata.identity.name,
|
|
85
|
-
GIT_COMMITTER_EMAIL: metadata.identity.email,
|
|
86
|
-
GIT_AUTHOR_DATE: metadata.at,
|
|
87
|
-
GIT_COMMITTER_DATE: metadata.at,
|
|
88
|
-
}))
|
|
89
|
-
.toString("utf8")
|
|
90
|
-
.trim();
|
|
91
|
-
return mintSnapshotId(commit);
|
|
105
|
+
return await materializeSnapshotCommit(repository, tree, [parent], metadata.message, deliveryEnvironment(metadata));
|
|
92
106
|
}
|
|
93
107
|
export async function persistedTender(repository, snapshot) {
|
|
94
|
-
const tree = (await
|
|
108
|
+
const tree = (await executeGit(repository, ["show", "-s", "--format=%T", gitObjectIdForSnapshot(snapshot)]))
|
|
95
109
|
.toString("ascii")
|
|
96
110
|
.trim();
|
|
97
111
|
return { head: snapshot, tree: gitObjectId(tree, "persisted tender tree") };
|
|
@@ -100,7 +114,7 @@ export async function materializeReintegrationSnapshot(repository, tree, parent,
|
|
|
100
114
|
const templateObject = gitObjectIdForSnapshot(template);
|
|
101
115
|
let raw;
|
|
102
116
|
try {
|
|
103
|
-
raw = await
|
|
117
|
+
raw = await executeGit(repository, ["cat-file", "commit", templateObject]);
|
|
104
118
|
}
|
|
105
119
|
catch (error) {
|
|
106
120
|
if (error instanceof GitPlumbingError) {
|
|
@@ -114,7 +128,7 @@ export async function materializeReintegrationSnapshot(repository, tree, parent,
|
|
|
114
128
|
const message = raw.subarray(separator + 2);
|
|
115
129
|
let fields;
|
|
116
130
|
try {
|
|
117
|
-
const formatted = (await
|
|
131
|
+
const formatted = (await executeGit(repository, ["show", "-s", "--format=%an%x00%ae%x00%aI%x00%cn%x00%ce%x00%cI", templateObject]))
|
|
118
132
|
.toString("utf8")
|
|
119
133
|
.replace(/\n$/u, "");
|
|
120
134
|
fields = formatted.split("\0");
|
|
@@ -129,17 +143,14 @@ export async function materializeReintegrationSnapshot(repository, tree, parent,
|
|
|
129
143
|
throw new AuthorityCorruptionError(`recorded delivery snapshot metadata is malformed: ${template}`);
|
|
130
144
|
}
|
|
131
145
|
const [authorName, authorEmail, authorDate, committerName, committerEmail, committerDate] = fields;
|
|
132
|
-
|
|
146
|
+
return await materializeSnapshotCommit(repository, tree, [parent], message, {
|
|
133
147
|
GIT_AUTHOR_NAME: authorName,
|
|
134
148
|
GIT_AUTHOR_EMAIL: authorEmail,
|
|
135
149
|
GIT_AUTHOR_DATE: authorDate,
|
|
136
150
|
GIT_COMMITTER_NAME: committerName,
|
|
137
151
|
GIT_COMMITTER_EMAIL: committerEmail,
|
|
138
152
|
GIT_COMMITTER_DATE: committerDate,
|
|
139
|
-
})
|
|
140
|
-
.toString("utf8")
|
|
141
|
-
.trim();
|
|
142
|
-
return mintSnapshotId(commit);
|
|
153
|
+
});
|
|
143
154
|
}
|
|
144
155
|
async function isAncestor(repository, ancestor, descendant) {
|
|
145
156
|
const result = await runAllowingNonzero(repository, [
|
|
@@ -302,7 +313,7 @@ export async function planIntegration(repository, input, tender, requireBranches
|
|
|
302
313
|
}
|
|
303
314
|
async function deliverySnapshotAvailability(repository, predecessor, candidate) {
|
|
304
315
|
const objects = [gitObjectIdForSnapshot(predecessor), gitObjectIdForSnapshot(candidate)];
|
|
305
|
-
const types = (await
|
|
316
|
+
const types = (await executeGit(repository, ["cat-file", "--batch-check=%(objectname) %(objecttype)"], `${objects.join("\n")}\n`))
|
|
306
317
|
.toString("ascii")
|
|
307
318
|
.trimEnd()
|
|
308
319
|
.split("\n")
|
|
@@ -323,7 +334,7 @@ export const deliveryDiffScopeSchema = z
|
|
|
323
334
|
})
|
|
324
335
|
.strict();
|
|
325
336
|
export async function readDeliveryScope(repository, predecessor, candidate, includePaths) {
|
|
326
|
-
const scope = decodeGitNumstat(await
|
|
337
|
+
const scope = decodeGitNumstat(await executeGit(repository, [
|
|
327
338
|
"diff",
|
|
328
339
|
"--numstat",
|
|
329
340
|
"-z",
|
|
@@ -334,7 +345,7 @@ export async function readDeliveryScope(repository, predecessor, candidate, incl
|
|
|
334
345
|
return scope;
|
|
335
346
|
return {
|
|
336
347
|
...scope,
|
|
337
|
-
paths: decodeGitNameOnly(await
|
|
348
|
+
paths: decodeGitNameOnly(await executeGit(repository, [
|
|
338
349
|
"diff",
|
|
339
350
|
"--name-only",
|
|
340
351
|
"--no-renames",
|
|
@@ -349,7 +360,7 @@ export async function readDeliveryDiff(repository, predecessor, candidate) {
|
|
|
349
360
|
if ((await deliverySnapshotAvailability(repository, predecessor, candidate)) === "unavailable")
|
|
350
361
|
return null;
|
|
351
362
|
try {
|
|
352
|
-
return (await
|
|
363
|
+
return (await executeGit(repository, [
|
|
353
364
|
"diff",
|
|
354
365
|
"--no-ext-diff",
|
|
355
366
|
"--no-color",
|
package/build/src/git/nuke.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { access, lstat, realpath, rm } from "node:fs/promises";
|
|
2
2
|
import { isAbsolute, relative, sep } from "node:path";
|
|
3
3
|
import { nukeEmptyPlaceAuthority, readPlaceRegister, releaseManagedWorktrees, withPlaceAuthorityFence, } from "../workspace-place.js";
|
|
4
|
-
import { GitPlumbingError,
|
|
4
|
+
import { GitPlumbingError, executeGit } from "./process.js";
|
|
5
5
|
import { confirmPrivateStatePublication, withPrivateStatePublicationSeat, } from "./private-state-seat.js";
|
|
6
6
|
import { CANDIDATE_PIN_REF_NAMESPACE, DELIVERY_REF_NAMESPACE, GIT_REF, NoGitWorldError, readRef, registeredWorktrees, repositoryAt, worktreeGitDirectory, } from "./repository.js";
|
|
7
7
|
import { worktreePath } from "./workspace.js";
|
|
@@ -89,7 +89,7 @@ async function removeManagedWorktree(repository, entry) {
|
|
|
89
89
|
return await removeUnregisteredResidue(repository, entry);
|
|
90
90
|
if (registered.branch !== null)
|
|
91
91
|
return false;
|
|
92
|
-
await
|
|
92
|
+
await executeGit(repository, ["worktree", "remove", "--force", entry.path]);
|
|
93
93
|
if ((await registeredWorktrees(repository)).some((candidate) => candidate.path === entry.path)) {
|
|
94
94
|
throw new GitResetStopError(`managed Place worktree remains registered: ${entry.path}`);
|
|
95
95
|
}
|
|
@@ -104,7 +104,7 @@ async function removeManagedWorktree(repository, entry) {
|
|
|
104
104
|
throw new GitResetStopError(`managed Place worktree remains present: ${entry.path}`);
|
|
105
105
|
}
|
|
106
106
|
async function deleteRefAt(repository, ref, expectedOid) {
|
|
107
|
-
await
|
|
107
|
+
await executeGit(repository, ["update-ref", "--no-deref", "-d", ref, expectedOid]);
|
|
108
108
|
}
|
|
109
109
|
async function deleteObservedStateRef(repository, expectedOid, seat) {
|
|
110
110
|
try {
|
|
@@ -126,7 +126,7 @@ async function removeOwnedRefs(repository, onRemoved) {
|
|
|
126
126
|
let removed = 0;
|
|
127
127
|
const roots = [DELIVERY_REF_NAMESPACE, CANDIDATE_PIN_REF_NAMESPACE];
|
|
128
128
|
for (const root of roots) {
|
|
129
|
-
const refs = (await
|
|
129
|
+
const refs = (await executeGit(repository, ["for-each-ref", "--format=%(refname)", root]))
|
|
130
130
|
.toString("utf8")
|
|
131
131
|
.split("\n")
|
|
132
132
|
.filter((ref) => ref.length > root.length && ref.startsWith(`${root}/`));
|
|
@@ -3,7 +3,7 @@ import type { ContractsObservation } from "../core/facts/observation.js";
|
|
|
3
3
|
import { type GitSnapshot, type GitOid } from "./repository.js";
|
|
4
4
|
import { type GitRepository } from "./process.js";
|
|
5
5
|
import { type GitDecodeChannel, type GitReadObservation, type GitTreeSelection } from "./read-observation.js";
|
|
6
|
-
|
|
6
|
+
type TargetObservation = Readonly<{
|
|
7
7
|
head: SnapshotId | null;
|
|
8
8
|
drift: boolean;
|
|
9
9
|
}>;
|
|
@@ -12,11 +12,11 @@ type GitJournalRecord = Readonly<{
|
|
|
12
12
|
entries: readonly JournalEntry[];
|
|
13
13
|
state: ContractState | null;
|
|
14
14
|
}>;
|
|
15
|
-
|
|
15
|
+
type ContractWorldObservation = Readonly<{
|
|
16
16
|
snapshot: SnapshotId | null;
|
|
17
17
|
contracts: ReadonlyMap<ContractId, GitJournalRecord>;
|
|
18
18
|
}>;
|
|
19
|
-
|
|
19
|
+
type ActiveContractWorldObservation = ContractWorldObservation & Readonly<{
|
|
20
20
|
eligibility: ContractsObservation;
|
|
21
21
|
}>;
|
|
22
22
|
export type GitAdmissionSnapshot = Readonly<{
|
|
@@ -34,7 +34,7 @@ export type BindCoordinatesObservation = Readonly<{
|
|
|
34
34
|
target?: string;
|
|
35
35
|
branch: string | null;
|
|
36
36
|
}>;
|
|
37
|
-
|
|
37
|
+
type UnbornHeadObservation = Readonly<{
|
|
38
38
|
kind: "unborn-head";
|
|
39
39
|
branch: string | null;
|
|
40
40
|
}>;
|
package/build/src/git/observe.js
CHANGED
|
@@ -3,7 +3,7 @@ import { AuthorityCorruptionError } from "../core/facts/errors.js";
|
|
|
3
3
|
import { foldJournal } from "../core/facts/fold.js";
|
|
4
4
|
import { contractJournalPath, contractJournalPaths, mintContractHead, mintSnapshotId, } from "./identity.js";
|
|
5
5
|
import { isKeiyakuOwnedRef } from "./repository.js";
|
|
6
|
-
import { GitPlumbingError,
|
|
6
|
+
import { GitPlumbingError, executeGit } from "./process.js";
|
|
7
7
|
import { readBlobResults, readGitTreeSelection, withGitTargetedReadObservation, } from "./read-observation.js";
|
|
8
8
|
export async function observeDeliveryTargetAt(observation, state) {
|
|
9
9
|
const target = state.coordinates.target;
|
|
@@ -29,7 +29,7 @@ export async function normalizeTargetBranch(repository, input) {
|
|
|
29
29
|
if (branch.length === 0 || branch.startsWith("-") || isKeiyakuOwnedRef(target))
|
|
30
30
|
return null;
|
|
31
31
|
try {
|
|
32
|
-
const checked = (await
|
|
32
|
+
const checked = (await executeGit(repository, ["check-ref-format", "--branch", branch])).toString("utf8").trim();
|
|
33
33
|
return checked === branch ? target : null;
|
|
34
34
|
}
|
|
35
35
|
catch (error) {
|
|
@@ -41,7 +41,7 @@ export async function normalizeTargetBranch(repository, input) {
|
|
|
41
41
|
/** Read one worktree's attached branch, or null for detached HEAD. */
|
|
42
42
|
export async function currentBranch(repository, path) {
|
|
43
43
|
try {
|
|
44
|
-
const ref = (await
|
|
44
|
+
const ref = (await executeGit(repository, [...(path === undefined ? [] : ["-C", path]), "symbolic-ref", "--quiet", "HEAD"]))
|
|
45
45
|
.toString("utf8")
|
|
46
46
|
.trim();
|
|
47
47
|
return ref.startsWith("refs/heads/") ? ref : null;
|
|
@@ -64,7 +64,7 @@ function structuredFields(output, fieldCount) {
|
|
|
64
64
|
async function observeHeadCoordinates(repository) {
|
|
65
65
|
try {
|
|
66
66
|
return {
|
|
67
|
-
start: mintSnapshotId((await
|
|
67
|
+
start: mintSnapshotId((await executeGit(repository, ["rev-parse", "--verify", "HEAD"])).toString("utf8").trim()),
|
|
68
68
|
branch: await currentBranch(repository),
|
|
69
69
|
};
|
|
70
70
|
}
|
|
@@ -84,7 +84,7 @@ export async function observeBindCoordinates(repository, selection = { kind: "ta
|
|
|
84
84
|
if (isKeiyakuOwnedRef(requestedTarget)) {
|
|
85
85
|
throw new Error(`bind target names a Keiyaku-owned ref: ${requestedTarget}`);
|
|
86
86
|
}
|
|
87
|
-
const output = await
|
|
87
|
+
const output = await executeGit(repository, [
|
|
88
88
|
"for-each-ref",
|
|
89
89
|
`--format=${TARGET_COORDINATES_FORMAT}`,
|
|
90
90
|
"--",
|
|
@@ -16,8 +16,6 @@ export type GitRepository = Readonly<{
|
|
|
16
16
|
/** Test-only hook after the private-state seat close attempt. */
|
|
17
17
|
readonly onPrivateStateSeatClose?: () => void;
|
|
18
18
|
}>;
|
|
19
|
-
/** Give one caller's Git capability view its cancellation boundary. */
|
|
20
|
-
export declare function withGitAbortSignal(repository: GitRepository, signal: AbortSignal | undefined): GitRepository;
|
|
21
19
|
export declare class GitPlumbingError extends Error {
|
|
22
20
|
readonly stdout: Buffer;
|
|
23
21
|
readonly stderr: Buffer;
|
|
@@ -31,8 +29,7 @@ export declare class GitPlumbingError extends Error {
|
|
|
31
29
|
pid?: number | null;
|
|
32
30
|
}>);
|
|
33
31
|
}
|
|
34
|
-
export declare function
|
|
32
|
+
export declare function executeGit(repository: GitRepository, args: readonly string[], input?: string | Uint8Array, environment?: NodeJS.ProcessEnv): Promise<Buffer>;
|
|
35
33
|
/** Connect two owned Git commands without materializing the intermediate output. */
|
|
36
34
|
export declare function runGitPipe(repository: GitRepository, sourceArgs: readonly string[], sinkArgs: readonly string[]): Promise<Buffer>;
|
|
37
35
|
export declare function consumeGitStdout(repository: GitRepository, args: readonly string[], consume: (chunk: Buffer) => void): Promise<void>;
|
|
38
|
-
export declare function runGitWithEnvironment(repository: GitRepository, args: readonly string[], input: string | Uint8Array | undefined, environment: NodeJS.ProcessEnv): Promise<Buffer>;
|
package/build/src/git/process.js
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import { consumeProcessStdout, spawnCancellableProcess } from "../runtime/proc/run.js";
|
|
2
2
|
import { pipeline } from "node:stream/promises";
|
|
3
|
-
/** Give one caller's Git capability view its cancellation boundary. */
|
|
4
|
-
export function withGitAbortSignal(repository, signal) {
|
|
5
|
-
return signal === undefined ? repository : { ...repository, signal };
|
|
6
|
-
}
|
|
7
3
|
export class GitPlumbingError extends Error {
|
|
8
4
|
stdout;
|
|
9
5
|
stderr;
|
|
@@ -32,7 +28,7 @@ function commandError(command, error) {
|
|
|
32
28
|
pid: candidate.pid ?? null,
|
|
33
29
|
});
|
|
34
30
|
}
|
|
35
|
-
async function executeGit(repository, args, input, environment) {
|
|
31
|
+
export async function executeGit(repository, args, input, environment) {
|
|
36
32
|
if (repository.signal?.aborted === true) {
|
|
37
33
|
throw commandError(args, { message: "git process cancelled", status: null });
|
|
38
34
|
}
|
|
@@ -81,9 +77,6 @@ async function executeGit(repository, args, input, environment) {
|
|
|
81
77
|
pid: child.pid,
|
|
82
78
|
});
|
|
83
79
|
}
|
|
84
|
-
export async function runGit(repository, args, input) {
|
|
85
|
-
return await executeGit(repository, args, input);
|
|
86
|
-
}
|
|
87
80
|
function boundedPipeOutput(child, args) {
|
|
88
81
|
const stdout = [];
|
|
89
82
|
let bytes = 0;
|
|
@@ -212,6 +205,3 @@ export async function consumeGitStdout(repository, args, consume) {
|
|
|
212
205
|
pid: result.pid ?? undefined,
|
|
213
206
|
});
|
|
214
207
|
}
|
|
215
|
-
export async function runGitWithEnvironment(repository, args, input, environment) {
|
|
216
|
-
return await executeGit(repository, args, input, environment);
|
|
217
|
-
}
|
|
@@ -6,7 +6,7 @@ import { GIT_FORMAT_BYTES, GIT_FORMAT_PATH, GIT_REF, readRef } from "./repositor
|
|
|
6
6
|
import { GitPlumbingError } from "./process.js";
|
|
7
7
|
const gitReadObservationBrand = Symbol("GitReadObservation");
|
|
8
8
|
const gitDecodeChannelBrand = Symbol("GitDecodeChannel");
|
|
9
|
-
const BATCH_RETIREMENT_GRACE_MS =
|
|
9
|
+
const BATCH_RETIREMENT_GRACE_MS = 5_000;
|
|
10
10
|
function streamCursor(stream) {
|
|
11
11
|
const iterator = stream[Symbol.asyncIterator]();
|
|
12
12
|
let buffer = Buffer.alloc(0);
|
|
@@ -142,7 +142,7 @@ type ReconcileBatchItem = Readonly<{
|
|
|
142
142
|
state: ContractState | null;
|
|
143
143
|
result: ReconcileResult;
|
|
144
144
|
}>;
|
|
145
|
-
|
|
145
|
+
type GitReconcileObservation = Readonly<{
|
|
146
146
|
state: ContractState | null;
|
|
147
147
|
result: ReconcileResult;
|
|
148
148
|
}>;
|
|
@@ -8,7 +8,7 @@ import { dirname, join } from "node:path";
|
|
|
8
8
|
import { acquireSqliteTransactionLock, SqliteTransactionLockError, } from "../coordination/sqlite-transaction-lock.js";
|
|
9
9
|
import { AuthorityCorruptionError } from "../core/facts/errors.js";
|
|
10
10
|
import { CANDIDATE_PIN_REF_NAMESPACE, commonGitDirectory, DELIVERY_REF_NAMESPACE, registeredWorktreePaths, } from "./repository.js";
|
|
11
|
-
import {
|
|
11
|
+
import { executeGit, GitPlumbingError } from "./process.js";
|
|
12
12
|
import { contractLocator, contractPhysicalName, gitObjectIdForSnapshot } from "./identity.js";
|
|
13
13
|
import { observeContractAt } from "./observe.js";
|
|
14
14
|
import { acquireTargetPlacementFence, recoverTargetPlacement } from "./target-placement.js";
|
|
@@ -158,25 +158,27 @@ async function worktree(repository, topology, path, desired) {
|
|
|
158
158
|
if (registered && (await pathExists(path)))
|
|
159
159
|
return { kind: "worktree", path, action: "unchanged" };
|
|
160
160
|
if (registered) {
|
|
161
|
-
await
|
|
161
|
+
await executeGit(repository, ["worktree", "remove", path]);
|
|
162
162
|
topology.paths.delete(path);
|
|
163
163
|
}
|
|
164
164
|
if (await pathExists(path))
|
|
165
165
|
throw new WorktreeCustodyError(`delivery worktree path is occupied: ${path}`);
|
|
166
166
|
await mkdir(dirname(path), { recursive: true });
|
|
167
|
-
await
|
|
167
|
+
await executeGit(repository, ["worktree", "add", "--detach", path, gitObjectIdForSnapshot(desired)]);
|
|
168
168
|
topology.paths.add(path);
|
|
169
169
|
return { kind: "worktree", path, action: "created" };
|
|
170
170
|
}
|
|
171
|
-
async function removeCollectableScratch(repository, topology, acc) {
|
|
172
|
-
for (const removal of await removeCollectableScratchWorktrees(repository, topology.paths)) {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
171
|
+
async function removeCollectableScratch(repository, topology, acc, terminalContract) {
|
|
172
|
+
for (const removal of await removeCollectableScratchWorktrees(repository, topology.paths, terminalContract)) {
|
|
173
|
+
if (!removal.retained) {
|
|
174
|
+
acc.recordEffect({ kind: "worktree", path: removal.path, action: "removed" });
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
acc.recordLag({
|
|
178
|
+
kind: "worktree-retained",
|
|
179
|
+
path: removal.path,
|
|
180
|
+
...(removal.diagnostic === undefined ? {} : { diagnostic: removal.diagnostic }),
|
|
181
|
+
});
|
|
180
182
|
}
|
|
181
183
|
}
|
|
182
184
|
async function reconcileTargetCheckouts(repository, state, onPhysical) {
|
|
@@ -288,7 +290,7 @@ async function reconcileWithTopology(input, state, topology) {
|
|
|
288
290
|
const acc = reconcileAccumulation(input.onPhysical);
|
|
289
291
|
const { effects, lag, hookRuns } = acc;
|
|
290
292
|
try {
|
|
291
|
-
await removeCollectableScratch(repository, topology, acc);
|
|
293
|
+
await removeCollectableScratch(repository, topology, acc, state?.terminal ? state.id : undefined);
|
|
292
294
|
if (!state)
|
|
293
295
|
return complete(effects, lag);
|
|
294
296
|
const targetCheckouts = await reconcileTargetCheckouts(repository, state, input.onPhysical);
|
|
@@ -7,7 +7,6 @@ export declare const CANDIDATE_PIN_REF_NAMESPACE = "refs/keiyaku/candidate";
|
|
|
7
7
|
export declare const HANDOFF_RECEIPT_REF_NAMESPACE = "refs/keiyaku/handoff";
|
|
8
8
|
export declare const GIT_FORMAT_PATH = "meta/format.json";
|
|
9
9
|
export declare const GIT_FORMAT_BYTES = "{\"version\":4}\n";
|
|
10
|
-
export declare const STATE_COMMIT_SUBJECT = "keiyaku authority - do not delete or rewrite";
|
|
11
10
|
export declare function isKeiyakuOwnedRef(ref: string): boolean;
|
|
12
11
|
export type GitOid = string;
|
|
13
12
|
export type RefPublication = Readonly<{
|
|
@@ -31,7 +30,7 @@ export declare class NoGitWorldError extends Error {
|
|
|
31
30
|
readonly path: string;
|
|
32
31
|
constructor(path: string);
|
|
33
32
|
}
|
|
34
|
-
|
|
33
|
+
type RegisteredWorktree = Readonly<{
|
|
35
34
|
path: string;
|
|
36
35
|
branch: string | null;
|
|
37
36
|
}>;
|
|
@@ -40,12 +39,9 @@ export declare function registeredWorktreePaths(repository: GitRepository): Prom
|
|
|
40
39
|
export declare function repositoryAt(cwd: string, gitPath?: string): Promise<GitRepository>;
|
|
41
40
|
export declare function commonGitDirectory(repository: GitRepository): string;
|
|
42
41
|
export declare function worktreeGitDirectory(repository: GitRepository, worktree: string): Promise<string>;
|
|
43
|
-
export declare function worktreeRoot(repository: GitRepository): Promise<string>;
|
|
44
42
|
export declare function readRef(repository: GitRepository, ref: string): Promise<GitOid | null>;
|
|
45
43
|
/** Read an explicit set of asserted refs from one fresh Git observation. */
|
|
46
44
|
export declare function readRefs(repository: GitRepository, refs: readonly string[]): Promise<Map<any, any>>;
|
|
47
|
-
export declare function readTreeEntries(repository: GitRepository, tree: GitOid): Promise<Map<string, TreeEntry>>;
|
|
48
|
-
export declare function readGit(repository: GitRepository): Promise<GitSnapshot>;
|
|
49
45
|
/** Read only the requested private Git paths from one immutable Git tree. */
|
|
50
46
|
export declare function readGitPaths(repository: GitRepository, requestedPaths: readonly string[]): Promise<GitSnapshot>;
|
|
51
47
|
export declare function writeBlob(repository: GitRepository, bytes: string | Uint8Array): Promise<GitOid>;
|
|
@@ -74,7 +70,7 @@ export declare function updateRefsAtomically(repository: GitRepository, updates:
|
|
|
74
70
|
readonly newOid: GitOid;
|
|
75
71
|
readonly expectedOid: GitOid;
|
|
76
72
|
})[], assertions?: readonly GitRefAssertion[]): Promise<RefPublication>;
|
|
77
|
-
|
|
73
|
+
type GitDiffStat = Readonly<{
|
|
78
74
|
filesChanged: number;
|
|
79
75
|
insertions: number;
|
|
80
76
|
deletions: number;
|
|
@@ -2,7 +2,7 @@ import { isAbsolute, resolve } from "node:path";
|
|
|
2
2
|
import { realpath } from "node:fs/promises";
|
|
3
3
|
import { AuthorityCorruptionError } from "../core/facts/errors.js";
|
|
4
4
|
import { gitObjectId } from "./identity.js";
|
|
5
|
-
import {
|
|
5
|
+
import { executeGit, GitPlumbingError } from "./process.js";
|
|
6
6
|
import { parseTreeObject, prepareTreeUpdate, treeUpdate, validPath, } from "./tree.js";
|
|
7
7
|
export const GIT_REF = "refs/heads/keiyaku-state";
|
|
8
8
|
export const DELIVERY_REF_NAMESPACE = "refs/keiyaku/delivery";
|
|
@@ -11,7 +11,7 @@ export const HANDOFF_RECEIPT_REF_NAMESPACE = "refs/keiyaku/handoff";
|
|
|
11
11
|
export const GIT_FORMAT_PATH = "meta/format.json";
|
|
12
12
|
const CURRENT_FORMAT_VERSION = 4;
|
|
13
13
|
export const GIT_FORMAT_BYTES = `{"version":${CURRENT_FORMAT_VERSION}}\n`;
|
|
14
|
-
|
|
14
|
+
const STATE_COMMIT_SUBJECT = "keiyaku authority - do not delete or rewrite";
|
|
15
15
|
export function isKeiyakuOwnedRef(ref) {
|
|
16
16
|
return (ref === GIT_REF ||
|
|
17
17
|
ref === DELIVERY_REF_NAMESPACE ||
|
|
@@ -67,7 +67,7 @@ function worktreesFromPorcelain(output) {
|
|
|
67
67
|
return worktrees;
|
|
68
68
|
}
|
|
69
69
|
export async function registeredWorktrees(repository) {
|
|
70
|
-
return await Promise.all(worktreesFromPorcelain(await
|
|
70
|
+
return await Promise.all(worktreesFromPorcelain(await executeGit(repository, ["worktree", "list", "--porcelain", "-z"])).map(async (worktree) => {
|
|
71
71
|
const { prunable, ...registered } = worktree;
|
|
72
72
|
try {
|
|
73
73
|
return { ...registered, path: await realpath(registered.path) };
|
|
@@ -111,7 +111,9 @@ export async function repositoryAt(cwd, gitPath = "git") {
|
|
|
111
111
|
return { gitPath, effectiveCwd, invocationWorktree, primaryWorktree, commonDirectory };
|
|
112
112
|
}
|
|
113
113
|
async function absoluteGitPath(repository, args, label) {
|
|
114
|
-
const value = (await
|
|
114
|
+
const value = (await executeGit(repository, ["rev-parse", "--path-format=absolute", ...args]))
|
|
115
|
+
.toString("utf8")
|
|
116
|
+
.trim();
|
|
115
117
|
if (value.length === 0)
|
|
116
118
|
throw new Error(`${label} is empty`);
|
|
117
119
|
return await realpath(resolve(isAbsolute(value) ? value : resolve(repository.effectiveCwd, value)));
|
|
@@ -123,7 +125,7 @@ export async function worktreeGitDirectory(repository, worktree) {
|
|
|
123
125
|
const scoped = { ...repository, effectiveCwd: worktree };
|
|
124
126
|
return await absoluteGitPath(scoped, ["--git-dir"], "worktree Git directory");
|
|
125
127
|
}
|
|
126
|
-
|
|
128
|
+
async function worktreeRoot(repository) {
|
|
127
129
|
return await absoluteGitPath(repository, ["--show-toplevel"], "worktree root");
|
|
128
130
|
}
|
|
129
131
|
function assertRef(ref) {
|
|
@@ -138,7 +140,7 @@ function assertAssertionRef(ref) {
|
|
|
138
140
|
export async function readRef(repository, ref) {
|
|
139
141
|
assertAssertionRef(ref);
|
|
140
142
|
try {
|
|
141
|
-
const oid = (await
|
|
143
|
+
const oid = (await executeGit(repository, ["rev-parse", "--verify", "--quiet", ref])).toString("utf8").trim();
|
|
142
144
|
return oid.length === 0 ? null : gitObjectId(oid, `ref ${ref}`);
|
|
143
145
|
}
|
|
144
146
|
catch (error) {
|
|
@@ -154,7 +156,7 @@ export async function readRefs(repository, refs) {
|
|
|
154
156
|
assertAssertionRef(ref);
|
|
155
157
|
if (refsToRead.length === 0)
|
|
156
158
|
return new Map();
|
|
157
|
-
const output = (await
|
|
159
|
+
const output = (await executeGit(repository, ["cat-file", "--batch-check=%(objectname)"], `${refsToRead.join("\n")}\n`)).toString("utf8");
|
|
158
160
|
const records = output.split("\n");
|
|
159
161
|
if (records.pop() !== "" || records.length !== refsToRead.length)
|
|
160
162
|
throw new Error("Git ref batch output is malformed");
|
|
@@ -168,7 +170,7 @@ export async function readRefs(repository, refs) {
|
|
|
168
170
|
}
|
|
169
171
|
async function readTreeForCommit(repository, commit) {
|
|
170
172
|
gitObjectId(commit, "Git commit");
|
|
171
|
-
const tree = (await
|
|
173
|
+
const tree = (await executeGit(repository, ["show", "-s", "--format=%T", commit])).toString("utf8").trim();
|
|
172
174
|
gitObjectId(tree, "Git tree");
|
|
173
175
|
return tree;
|
|
174
176
|
}
|
|
@@ -190,21 +192,6 @@ function parseTreeEntries(output) {
|
|
|
190
192
|
}
|
|
191
193
|
return entries;
|
|
192
194
|
}
|
|
193
|
-
export async function readTreeEntries(repository, tree) {
|
|
194
|
-
gitObjectId(tree, "tree");
|
|
195
|
-
const output = await runGit(repository, ["ls-tree", "-r", "-z", "--full-tree", tree]);
|
|
196
|
-
return parseTreeEntries(output);
|
|
197
|
-
}
|
|
198
|
-
export async function readGit(repository) {
|
|
199
|
-
const commit = await readRef(repository, GIT_REF);
|
|
200
|
-
if (commit === null) {
|
|
201
|
-
return { commit: null, tree: null, paths: new Map() };
|
|
202
|
-
}
|
|
203
|
-
const tree = await readTreeForCommit(repository, commit);
|
|
204
|
-
const paths = await readTreeEntries(repository, tree);
|
|
205
|
-
await validateGitFormat(repository, paths);
|
|
206
|
-
return { commit, tree, paths };
|
|
207
|
-
}
|
|
208
195
|
/** Read only the requested private Git paths from one immutable Git tree. */
|
|
209
196
|
export async function readGitPaths(repository, requestedPaths) {
|
|
210
197
|
for (const path of requestedPaths)
|
|
@@ -213,18 +200,18 @@ export async function readGitPaths(repository, requestedPaths) {
|
|
|
213
200
|
if (commit === null)
|
|
214
201
|
return { commit: null, tree: null, paths: new Map() };
|
|
215
202
|
const tree = await readTreeForCommit(repository, commit);
|
|
216
|
-
const paths = parseTreeEntries(await
|
|
203
|
+
const paths = parseTreeEntries(await executeGit(repository, ["ls-tree", "-z", "--full-tree", tree, "--", GIT_FORMAT_PATH, ...requestedPaths]));
|
|
217
204
|
await validateGitFormat(repository, paths);
|
|
218
205
|
return { commit, tree, paths };
|
|
219
206
|
}
|
|
220
207
|
export async function writeBlob(repository, bytes) {
|
|
221
|
-
const oid = (await
|
|
208
|
+
const oid = (await executeGit(repository, ["hash-object", "-w", "--stdin"], bytes)).toString("utf8").trim();
|
|
222
209
|
gitObjectId(oid, "written blob");
|
|
223
210
|
return oid;
|
|
224
211
|
}
|
|
225
212
|
export async function readBlob(repository, oid) {
|
|
226
213
|
gitObjectId(oid, "blob");
|
|
227
|
-
return await
|
|
214
|
+
return await executeGit(repository, ["cat-file", "blob", oid]);
|
|
228
215
|
}
|
|
229
216
|
function malformedBatchOutput(detail) {
|
|
230
217
|
throw new GitPlumbingError({
|
|
@@ -239,7 +226,7 @@ async function readObjects(repository, oids) {
|
|
|
239
226
|
gitObjectId(oid, "Git object");
|
|
240
227
|
if (unique.length === 0)
|
|
241
228
|
return new Map();
|
|
242
|
-
const output = await
|
|
229
|
+
const output = await executeGit(repository, ["cat-file", "--batch"], `${unique.join("\n")}\n`);
|
|
243
230
|
const objects = new Map();
|
|
244
231
|
let offset = 0;
|
|
245
232
|
for (const oid of unique) {
|
|
@@ -290,7 +277,7 @@ async function baseTreeEntries(repository, baseTree, changedPaths, nodePaths) {
|
|
|
290
277
|
const byPath = new Map();
|
|
291
278
|
if (baseTree === null)
|
|
292
279
|
return byPath;
|
|
293
|
-
const ancestry = parseTreeEntries(await
|
|
280
|
+
const ancestry = parseTreeEntries(await executeGit(repository, [
|
|
294
281
|
"ls-tree",
|
|
295
282
|
"-z",
|
|
296
283
|
"-t",
|
|
@@ -323,7 +310,7 @@ async function baseTreeEntries(repository, baseTree, changedPaths, nodePaths) {
|
|
|
323
310
|
return byPath;
|
|
324
311
|
}
|
|
325
312
|
async function writePreparedTrees(repository, prepared) {
|
|
326
|
-
const output = await
|
|
313
|
+
const output = await executeGit(repository, ["mktree", "--batch"], prepared.map(({ records }) => `${records}\n`).join(""));
|
|
327
314
|
const written = output.toString("ascii").trimEnd().split("\n");
|
|
328
315
|
if (written.length !== prepared.length) {
|
|
329
316
|
throw new GitPlumbingError({ stderr: output, status: null, message: "malformed mktree --batch output" });
|
|
@@ -364,7 +351,7 @@ async function writeCommitObject(input) {
|
|
|
364
351
|
const args = ["commit-tree", tree];
|
|
365
352
|
for (const parent of parents)
|
|
366
353
|
args.push("-p", parent);
|
|
367
|
-
const commit = (await
|
|
354
|
+
const commit = (await executeGit(repository, args, `${message}\n`, {
|
|
368
355
|
GIT_AUTHOR_NAME: actor,
|
|
369
356
|
GIT_AUTHOR_EMAIL: email,
|
|
370
357
|
GIT_COMMITTER_NAME: actor,
|
|
@@ -420,7 +407,7 @@ export async function updateRefsAtomically(repository, updates, assertions = [])
|
|
|
420
407
|
}
|
|
421
408
|
const lines = ["start", ...assertions.map(refAssertionLine), ...updates.map(refUpdateLine), "prepare", "commit", ""];
|
|
422
409
|
try {
|
|
423
|
-
await
|
|
410
|
+
await executeGit(repository, ["update-ref", "--stdin"], lines.join("\n"));
|
|
424
411
|
return { kind: "published" };
|
|
425
412
|
}
|
|
426
413
|
catch (error) {
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import type { SnapshotId } from "../core/facts/types.js";
|
|
2
|
+
import type { ContractId, SnapshotId } from "../core/facts/types.js";
|
|
3
3
|
import { type GitRepository } from "./process.js";
|
|
4
4
|
export type MaterializedScratchCandidate = Readonly<{
|
|
5
5
|
cwd: string;
|
|
6
|
-
dispose: () => Promise<WorktreeLeak | null>;
|
|
6
|
+
dispose: (retain?: boolean) => Promise<WorktreeLeak | null>;
|
|
7
7
|
}>;
|
|
8
8
|
export declare const worktreeLeakSchema: z.ZodObject<{
|
|
9
|
-
path: z.ZodString
|
|
9
|
+
path: z.ZodOptional<z.ZodString>;
|
|
10
10
|
diagnostic: z.ZodString;
|
|
11
11
|
}, z.core.$strict>;
|
|
12
12
|
export type WorktreeLeak = z.infer<typeof worktreeLeakSchema>;
|
|
13
13
|
type CollectableScratchRemoval = Readonly<{
|
|
14
14
|
path: string;
|
|
15
|
-
|
|
15
|
+
/** False only when Git removed the path and absence was confirmed; retained removals own a diagnostic. */
|
|
16
16
|
retained: boolean;
|
|
17
17
|
diagnostic?: string;
|
|
18
18
|
}>;
|
|
19
19
|
/** Remove only abandoned Verification scratch worktrees whose ownership locks are held. */
|
|
20
|
-
export declare function removeCollectableScratchWorktrees(repository: GitRepository, paths: Set<string
|
|
20
|
+
export declare function removeCollectableScratchWorktrees(repository: GitRepository, paths: Set<string>, terminalContract?: ContractId): Promise<readonly CollectableScratchRemoval[]>;
|
|
21
21
|
/** Materialize a disposable candidate worktree and own only its removal. */
|
|
22
|
-
export declare function materializeScratchCandidate(repository: GitRepository, candidate: SnapshotId): Promise<MaterializedScratchCandidate>;
|
|
22
|
+
export declare function materializeScratchCandidate(repository: GitRepository, candidate: SnapshotId, contract?: ContractId): Promise<MaterializedScratchCandidate>;
|
|
23
23
|
export {};
|