@astrosheep/keiyaku 2.9.9 → 2.9.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -182
- package/build/.tsbuildinfo +1 -1
- package/build/agents/harness/control-types.js +18 -1
- package/build/agents/harness/index.js +2 -0
- package/build/agents/harness/outcome-error.js +81 -0
- package/build/agents/harness/projection.js +2 -1
- package/build/agents/harness/router.js +5 -1
- package/build/agents/providers/claude-agent-sdk/adapter.js +18 -4
- package/build/agents/providers/claude-agent-sdk/registration.js +11 -1
- package/build/agents/providers/claude-agent-sdk/session.js +19 -5
- package/build/agents/providers/codex-app-server/adapter.js +11 -6
- package/build/agents/providers/codex-app-server/events.js +67 -6
- package/build/agents/providers/codex-app-server/registration.js +11 -1
- package/build/agents/providers/opencode-sdk/adapter.js +8 -1
- package/build/agents/providers/opencode-sdk/events.js +24 -2
- package/build/agents/providers/opencode-sdk/registration.js +11 -1
- package/build/agents/providers/pi/checkpoint.js +9 -1
- package/build/agents/providers/pi/events.js +2 -0
- package/build/agents/providers/pi/registration.js +14 -1
- package/build/cli/commands/akuma/akuma/handler.js +1 -1
- package/build/cli/commands/akuma/akuma/meta.js +3 -3
- package/build/cli/commands/akuma/catalog.js +2 -2
- package/build/cli/commands/akuma/{view → show}/handler.js +5 -5
- package/build/cli/commands/akuma/{view → show}/meta.js +3 -3
- package/build/cli/commands/akuma.js +1 -1
- package/build/cli/commands/contract/amend/handler.js +5 -4
- package/build/cli/commands/contract/arc/handler.js +5 -4
- package/build/cli/commands/contract/audit/handler.js +2 -2
- package/build/cli/commands/contract/bind/handler.js +12 -3
- package/build/cli/commands/contract/bind/meta.js +45 -15
- package/build/cli/commands/contract/forfeit/handler.js +9 -5
- package/build/cli/commands/contract/log/handler.js +2 -2
- package/build/cli/commands/contract/petition/handler.js +34 -7
- package/build/cli/commands/contract/renew/handler.js +7 -7
- package/build/cli/commands/contract/renew/meta.js +3 -3
- package/build/cli/commands/metadata.js +10 -6
- package/build/cli/commands/projection/call/meta.js +1 -1
- package/build/cli/commands/projection/catalog.js +2 -0
- package/build/cli/commands/projection/history/handler.js +21 -0
- package/build/cli/commands/projection/history/meta.js +9 -0
- package/build/cli/commands/projection/shared.js +3 -4
- package/build/cli/commands/projection/status/handler.js +3 -3
- package/build/cli/commands/projection/wait/handler.js +20 -11
- package/build/cli/commands/task/add/handler.js +14 -5
- package/build/cli/commands/task/add/meta.js +2 -2
- package/build/cli/commands/task/catalog.js +4 -0
- package/build/cli/commands/task/done/handler.js +1 -4
- package/build/cli/commands/task/done/meta.js +1 -1
- package/build/cli/commands/task/drop/meta.js +1 -1
- package/build/cli/commands/task/hold/handler.js +3 -0
- package/build/cli/commands/task/hold/meta.js +8 -0
- package/build/cli/commands/task/log/handler.js +1 -1
- package/build/cli/commands/task/log/meta.js +2 -2
- package/build/cli/commands/task/resume/handler.js +3 -0
- package/build/cli/commands/task/resume/meta.js +8 -0
- package/build/cli/commands/task/shared.js +39 -14
- package/build/cli/commands/task/show/handler.js +1 -1
- package/build/cli/commands/task/show/meta.js +1 -1
- package/build/cli/commands/task/start/meta.js +1 -1
- package/build/cli/commands/task/stop/handler.js +1 -4
- package/build/cli/commands/task/stop/meta.js +1 -1
- package/build/cli/commands/task/update/handler.js +11 -3
- package/build/cli/commands/task/update/meta.js +3 -3
- package/build/cli/completion.js +3 -2
- package/build/cli/flags.js +51 -14
- package/build/cli/help.js +4 -1
- package/build/cli/index.js +40 -25
- package/build/cli/parse-flags.js +40 -7
- package/build/cli/parse-metadata.js +5 -2
- package/build/cli/parse-selectors.js +2 -3
- package/build/cli/parse.js +40 -6
- package/build/cli/projection-address.js +12 -10
- package/build/cli/render/amendment-warning.js +8 -0
- package/build/cli/render/arc.js +6 -2
- package/build/cli/render/audit.js +400 -81
- package/build/cli/render/kanshi.js +6 -1
- package/build/cli/render/lifecycle.js +56 -0
- package/build/cli/render/misc.js +24 -28
- package/build/cli/render/petition.js +5 -0
- package/build/cli/render/projection-activity.js +3 -2
- package/build/cli/render/projection-history.js +64 -0
- package/build/cli/render/shared.js +23 -9
- package/build/cli/render/status.js +9 -16
- package/build/cli/render/task.js +51 -26
- package/build/cli/render/terminal-failure.js +3 -1
- package/build/cli/render/wait.js +3 -5
- package/build/cli/subagent-guard.js +1 -1
- package/build/cli/types.js +1 -1
- package/build/config/env-keys.js +2 -0
- package/build/config/env.js +23 -0
- package/build/config/settings/schema.js +3 -1
- package/build/core/address-carrier.js +89 -0
- package/build/core/addressing.js +255 -196
- package/build/core/amend.js +115 -38
- package/build/core/amendment-criteria.js +17 -0
- package/build/core/amendment-warning.js +10 -0
- package/build/core/arc.js +184 -83
- package/build/core/audit/candidate.js +110 -9
- package/build/core/audit/coordinates.js +120 -65
- package/build/core/audit/evidence.js +19 -23
- package/build/core/audit/facade.js +34 -11
- package/build/core/audit/report.js +5 -5
- package/build/core/bind-reconciliation.js +390 -0
- package/build/core/bind-workspace.js +12 -20
- package/build/core/bind.js +242 -273
- package/build/core/call/call.js +1 -0
- package/build/core/call/context.js +46 -71
- package/build/core/call/prompt.js +6 -3
- package/build/core/context.js +49 -35
- package/build/core/contract-carrier-runtime.js +408 -0
- package/build/core/contract-carrier.js +270 -0
- package/build/core/contract-view.js +33 -27
- package/build/core/contract.js +5 -1
- package/build/core/derived-replay.js +207 -0
- package/build/core/draft.js +125 -57
- package/build/core/forfeit.js +113 -56
- package/build/core/identifier-slug.js +63 -13
- package/build/core/ids.js +4 -4
- package/build/core/lifecycle-history.js +35 -0
- package/build/core/lifecycle-recovery.js +117 -0
- package/build/core/lifecycle-runner.js +377 -0
- package/build/core/log.js +37 -11
- package/build/core/outcome-base.js +181 -0
- package/build/core/process-group.js +139 -0
- package/build/core/projection/generation/database.js +82 -7
- package/build/core/projection/generation/model.js +9 -36
- package/build/core/projection/generation/projection-generation-continuation.js +10 -22
- package/build/core/projection/generation/projection-generation-execution.js +6 -5
- package/build/core/projection/index.js +5 -4
- package/build/core/projection/projection-activity.js +27 -8
- package/build/core/projection/projection-alias.js +23 -10
- package/build/core/projection/projection-core.js +21 -7
- package/build/core/projection/projection-execution-observer.js +91 -39
- package/build/core/projection/projection-history.js +474 -0
- package/build/core/projection/projection-runner-lock.js +12 -0
- package/build/core/projection/projection-status-observation.js +365 -0
- package/build/core/projection/projection-status.js +21 -358
- package/build/core/projection/projection-terminal-failure.js +13 -8
- package/build/core/projection/projection-wait.js +7 -2
- package/build/core/projection/tell/launch-store.js +244 -0
- package/build/core/projection/tell/model.js +3 -3
- package/build/core/projection/tell/store.js +70 -118
- package/build/core/registry.js +22 -87
- package/build/core/renew-build.js +424 -0
- package/build/core/renew-plan.js +239 -0
- package/build/core/renew-rewrite.js +77 -0
- package/build/core/renew.js +295 -520
- package/build/core/repository-ledger/accepted-fold-read.js +19 -0
- package/build/core/repository-ledger/accepted-tail.js +116 -0
- package/build/core/repository-ledger/admission.js +38 -0
- package/build/core/repository-ledger/authoritative-transaction.js +1 -0
- package/build/core/repository-ledger/claim-evidence.js +147 -0
- package/build/core/repository-ledger/codec.js +297 -0
- package/build/core/repository-ledger/current-state-store.js +853 -0
- package/build/core/repository-ledger/fold-repository.js +375 -0
- package/build/core/repository-ledger/fold.js +248 -0
- package/build/core/repository-ledger/git-object-store.js +243 -0
- package/build/core/repository-ledger/history-read.js +15 -0
- package/build/core/repository-ledger/incremental-read.js +153 -0
- package/build/core/repository-ledger/index.js +15 -0
- package/build/core/repository-ledger/inventory-plan.js +75 -0
- package/build/core/repository-ledger/inventory.js +310 -0
- package/build/core/repository-ledger/publication-admission.js +125 -0
- package/build/core/repository-ledger/publication.js +353 -0
- package/build/core/repository-ledger/read-model.js +469 -0
- package/build/core/repository-ledger/transaction-membership.js +78 -0
- package/build/core/repository-ledger/traversal.js +404 -0
- package/build/core/repository-ledger/write-transaction.js +339 -0
- package/build/core/seal.js +168 -161
- package/build/core/settlement/claim-delivery.js +42 -215
- package/build/core/settlement/claim.js +235 -153
- package/build/core/settlement/index.js +4 -4
- package/build/core/settlement/petition-claim-gates.js +52 -33
- package/build/core/settlement/petition-forfeit.js +12 -5
- package/build/core/settlement/petition-head-guard.js +8 -5
- package/build/core/settlement/petition-preview.js +68 -56
- package/build/core/settlement/petition-window.js +50 -0
- package/build/core/settlement/petition.js +252 -131
- package/build/core/settlement/queue-read-model.js +72 -95
- package/build/core/settlement/queue-seat-allocation.js +35 -126
- package/build/core/settlement/queue.js +1 -1
- package/build/core/settlement/settlement.js +172 -80
- package/build/core/settlement/verdict.js +112 -66
- package/build/core/settlement/verification-coordination.js +305 -0
- package/build/core/settlement/verification-supervisor.js +275 -0
- package/build/core/settlement/verification.js +398 -31
- package/build/core/status/board.js +114 -119
- package/build/core/status/lifecycle.js +7 -80
- package/build/core/structured-query.js +23 -0
- package/build/core/target-ref.js +1 -1
- package/build/core/task/board.js +59 -24
- package/build/core/task/commands.js +181 -33
- package/build/core/task/coordinate.js +56 -0
- package/build/core/task/document.js +25 -9
- package/build/core/task/index.js +7 -4
- package/build/core/task/query.js +17 -27
- package/build/core/task/settlement-git.js +164 -179
- package/build/core/task/settlement-policy.js +15 -11
- package/build/core/task/source-board.js +31 -13
- package/build/core/task/task-bind-preparation.js +193 -0
- package/build/core/task/task-contract.js +102 -193
- package/build/core/task/task-file-transaction.js +171 -0
- package/build/core/task/task-git-runtime.js +76 -14
- package/build/core/task/task-git-store.js +68 -169
- package/build/core/task/task-store-repository.js +13 -6
- package/build/core/task/task-worktree.js +6 -33
- package/build/core/task/task.js +1 -1
- package/build/core/task/tree.js +3 -3
- package/build/core/task/validation.js +4 -3
- package/build/core/transcripts.js +93 -1
- package/build/core/worktree-bootstrap.js +44 -36
- package/build/core/worktree-path.js +210 -77
- package/build/flow-error.js +7 -7
- package/build/generated/version.js +2 -2
- package/build/git/core.js +76 -13
- package/build/git/refs.js +168 -46
- package/build/git/streaming-batch.js +73 -13
- package/package.json +1 -1
- package/skills/keiyaku/SKILL.md +1 -1
- package/skills/keiyaku-akuma/SKILL.md +3 -3
- package/skills/keiyaku-task/SKILL.md +4 -2
- package/skills/keiyaku-workflow/SKILL.md +88 -203
- package/build/core/entry.js +0 -305
- package/build/core/ledger-batch.js +0 -194
- package/build/core/ledger.js +0 -84
- package/build/core/projection/leash.js +0 -87
- package/build/core/ref-log.js +0 -119
- package/build/core/renew-session.js +0 -128
- package/build/core/status/ledger-batch.js +0 -1
- package/build/core/status/reconciliation.js +0 -137
package/build/git/refs.js
CHANGED
|
@@ -1,11 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
const ZERO_OBJECT_ID = "0000000000000000000000000000000000000000";
|
|
1
|
+
import { isFullGitObjectId, runGitProcess, wrapGitError, } from "./core.js";
|
|
3
2
|
/** Normalize a configured branch to the sole local-branch ref spelling. */
|
|
4
3
|
export function normalizeLocalBranchRef(branch) {
|
|
5
4
|
return branch.startsWith("refs/heads/") ? branch : `refs/heads/${branch}`;
|
|
6
5
|
}
|
|
6
|
+
export const REF_TRANSACTION_CAS_MISMATCH = "REF_TRANSACTION_CAS_MISMATCH";
|
|
7
|
+
export const REF_TRANSACTION_NOT_COMMITTED = "REF_TRANSACTION_NOT_COMMITTED";
|
|
8
|
+
export const REF_TRANSACTION_INDETERMINATE = "REF_TRANSACTION_INDETERMINATE";
|
|
9
|
+
export class RefTransactionError extends Error {
|
|
10
|
+
code = "REF_TRANSACTION_FAILED";
|
|
11
|
+
constructor(message, options) {
|
|
12
|
+
super(message, options);
|
|
13
|
+
this.name = "RefTransactionError";
|
|
14
|
+
}
|
|
15
|
+
}
|
|
7
16
|
function isMissingExactRef(result) {
|
|
8
|
-
return result.
|
|
17
|
+
return !result.error
|
|
18
|
+
&& result.stdout === ""
|
|
19
|
+
&& result.status === 1;
|
|
9
20
|
}
|
|
10
21
|
function parseShowRefRows(output) {
|
|
11
22
|
const lines = output.split(/\r?\n/);
|
|
@@ -15,14 +26,18 @@ function parseShowRefRows(output) {
|
|
|
15
26
|
return null;
|
|
16
27
|
const rows = [];
|
|
17
28
|
for (const line of lines) {
|
|
18
|
-
const
|
|
19
|
-
|
|
29
|
+
const separator = line.indexOf(" ");
|
|
30
|
+
const objectId = separator < 0 ? "" : line.slice(0, separator);
|
|
31
|
+
const ref = separator < 0 ? "" : line.slice(separator + 1);
|
|
32
|
+
if (!isFullGitObjectId(objectId) || !/^refs\/\S+$/.test(ref))
|
|
20
33
|
return null;
|
|
21
|
-
rows.push({ objectId
|
|
34
|
+
rows.push({ objectId, ref });
|
|
22
35
|
}
|
|
23
36
|
return rows;
|
|
24
37
|
}
|
|
25
38
|
function throwGitProcessFailure(commandLabel, result, cwd) {
|
|
39
|
+
if (result.error?.name === "AbortError")
|
|
40
|
+
throw result.error;
|
|
26
41
|
const failure = result.error ?? Object.assign(new Error(result.stderr || `git ${commandLabel} exited ${result.status}`), {
|
|
27
42
|
stderr: result.stderr,
|
|
28
43
|
stdout: result.stdout,
|
|
@@ -46,58 +61,165 @@ export async function resolveLocalBranchHead(cwd, branch, run = runGitProcess) {
|
|
|
46
61
|
}
|
|
47
62
|
return throwGitProcessFailure(`show-ref ${ref}`, result, cwd);
|
|
48
63
|
}
|
|
49
|
-
async function
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
64
|
+
async function validateTransactionOperation(cwd, operation, objectIdLength) {
|
|
65
|
+
const refCheck = await runGitProcess(cwd, ["check-ref-format", operation.ref]);
|
|
66
|
+
if (refCheck.status !== 0 || refCheck.error) {
|
|
67
|
+
throw new RefTransactionError(`invalid transaction ref ${JSON.stringify(operation.ref)}`, { cause: refCheck.error });
|
|
53
68
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
69
|
+
const objectIds = operation.kind === "update"
|
|
70
|
+
? [["new", operation.newOid], ["old", operation.expectedOldOid]]
|
|
71
|
+
: [["old", operation.expectedOldOid]];
|
|
72
|
+
for (const [label, oid] of objectIds) {
|
|
73
|
+
if (oid !== null && (oid.length !== objectIdLength || !isFullGitObjectId(oid))) {
|
|
74
|
+
throw new RefTransactionError(`invalid ${label} object ID for ${operation.ref}`);
|
|
75
|
+
}
|
|
58
76
|
}
|
|
59
77
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if (
|
|
64
|
-
return
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
78
|
+
function formatTransactionOperation(operation, zero) {
|
|
79
|
+
if (operation.kind === "delete")
|
|
80
|
+
return `delete ${operation.ref} ${operation.expectedOldOid}`;
|
|
81
|
+
if (operation.kind === "verify")
|
|
82
|
+
return `verify ${operation.ref} ${operation.expectedOldOid ?? zero}`;
|
|
83
|
+
return `update ${operation.ref} ${operation.newOid} ${operation.expectedOldOid ?? zero}`;
|
|
84
|
+
}
|
|
85
|
+
function isUnambiguousCasRefusal(result) {
|
|
86
|
+
if (result.status === null || result.status === 0 || result.error)
|
|
87
|
+
return false;
|
|
88
|
+
return /cannot lock ref '[^']+': (?:is at [0-9a-f]+ but expected [0-9a-f]+|reference already exists|reference is missing but expected [0-9a-f]+)/i.test(result.stderr);
|
|
89
|
+
}
|
|
90
|
+
function isUnableToResolveReference(result) {
|
|
91
|
+
return result.status !== null
|
|
92
|
+
&& result.status !== 0
|
|
93
|
+
&& !result.error
|
|
94
|
+
&& /cannot lock ref '[^']+': unable to resolve reference/i.test(result.stderr);
|
|
95
|
+
}
|
|
96
|
+
function hasConclusiveMissingExpectedReference(observations) {
|
|
97
|
+
return observations.some((observation) => (observation.expectedOldOid !== null
|
|
98
|
+
&& observation.observed.status === "absent"));
|
|
99
|
+
}
|
|
100
|
+
function transactionFailureCause(result) {
|
|
101
|
+
return result.stderr.trim() || result.error?.message || `exit ${String(result.status)}`;
|
|
102
|
+
}
|
|
103
|
+
function requestedNewOid(operation) {
|
|
104
|
+
if (operation.kind === "update")
|
|
105
|
+
return operation.newOid;
|
|
106
|
+
if (operation.kind === "delete")
|
|
107
|
+
return null;
|
|
108
|
+
return operation.expectedOldOid;
|
|
109
|
+
}
|
|
110
|
+
function observationMatches(observation, expectedOid) {
|
|
111
|
+
if (observation.observed.status === "unknown")
|
|
112
|
+
return false;
|
|
113
|
+
if (expectedOid === null)
|
|
114
|
+
return observation.observed.status === "absent";
|
|
115
|
+
return observation.observed.status === "present" && observation.observed.objectId === expectedOid;
|
|
116
|
+
}
|
|
117
|
+
async function observeExactRef(cwd, ref) {
|
|
69
118
|
try {
|
|
70
|
-
await
|
|
119
|
+
const symbolic = await runGitProcess(cwd, ["symbolic-ref", "--quiet", ref]);
|
|
120
|
+
if (symbolic.status === 0 && !symbolic.error) {
|
|
121
|
+
const target = symbolic.stdout.trim();
|
|
122
|
+
return { status: "unknown", cause: `symbolic ref targets ${target || "an unreported ref"}` };
|
|
123
|
+
}
|
|
124
|
+
if (symbolic.error || (symbolic.status !== 1 && symbolic.status !== 0)) {
|
|
125
|
+
const cause = symbolic.stderr.trim() || symbolic.error?.message || `symbolic-ref exited ${String(symbolic.status)}`;
|
|
126
|
+
return { status: "unknown", cause };
|
|
127
|
+
}
|
|
128
|
+
const exact = await runGitProcess(cwd, ["show-ref", ref]);
|
|
129
|
+
if (exact.status === 0 && !exact.error) {
|
|
130
|
+
const exactRows = parseShowRefRows(exact.stdout)?.filter((row) => row.ref === ref);
|
|
131
|
+
if (exactRows?.length === 1) {
|
|
132
|
+
return { status: "present", objectId: exactRows[0].objectId };
|
|
133
|
+
}
|
|
134
|
+
return { status: "unknown", cause: `show-ref returned an invalid exact value for ${ref}` };
|
|
135
|
+
}
|
|
136
|
+
if (!exact.error && exact.status === 1 && exact.stdout === "")
|
|
137
|
+
return { status: "absent" };
|
|
138
|
+
const cause = exact.stderr.trim() || exact.error?.message || `show-ref exited ${String(exact.status)}`;
|
|
139
|
+
return { status: "unknown", cause };
|
|
71
140
|
}
|
|
72
141
|
catch (error) {
|
|
73
|
-
|
|
74
|
-
if (await resolveRefOrNull(cwd, ref) === objectId)
|
|
75
|
-
return;
|
|
76
|
-
throw wrapGitError(`update-ref ${ref}`, error, cwd);
|
|
142
|
+
return { status: "unknown", cause: error instanceof Error ? error.message : String(error) };
|
|
77
143
|
}
|
|
78
144
|
}
|
|
79
|
-
|
|
80
|
-
|
|
145
|
+
async function observeTransactionRefs(cwd, operations) {
|
|
146
|
+
return await Promise.all(operations.map(async (operation) => ({
|
|
147
|
+
ref: operation.ref,
|
|
148
|
+
expectedOldOid: operation.expectedOldOid,
|
|
149
|
+
requestedNewOid: requestedNewOid(operation),
|
|
150
|
+
observed: await observeExactRef(cwd, operation.ref),
|
|
151
|
+
})));
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Atomically apply a closed set of ref operations. Every old-OID assertion is
|
|
155
|
+
* submitted inside the same update-ref transaction as its operation; no
|
|
156
|
+
* preflight read is used as authority.
|
|
157
|
+
*/
|
|
158
|
+
export async function commitRefTransaction(cwd, operations, objectIdLength, options = {}) {
|
|
159
|
+
if (operations.length === 0)
|
|
160
|
+
throw new RefTransactionError("a ref transaction requires at least one operation");
|
|
161
|
+
const seen = new Set();
|
|
162
|
+
for (const operation of operations) {
|
|
163
|
+
await validateTransactionOperation(cwd, operation, objectIdLength);
|
|
164
|
+
if (seen.has(operation.ref))
|
|
165
|
+
throw new RefTransactionError(`duplicate transaction ref ${operation.ref}`);
|
|
166
|
+
seen.add(operation.ref);
|
|
167
|
+
}
|
|
168
|
+
const zero = "0".repeat(objectIdLength);
|
|
169
|
+
const input = [
|
|
170
|
+
"start",
|
|
171
|
+
...operations.map((operation) => formatTransactionOperation(operation, zero)),
|
|
172
|
+
"prepare",
|
|
173
|
+
"commit",
|
|
174
|
+
"",
|
|
175
|
+
].join("\n");
|
|
176
|
+
let result;
|
|
81
177
|
try {
|
|
82
|
-
|
|
83
|
-
if (existing === null)
|
|
84
|
-
return { ref, status: "already-absent" };
|
|
85
|
-
if (existing !== objectId) {
|
|
86
|
-
return {
|
|
87
|
-
ref,
|
|
88
|
-
status: "retained",
|
|
89
|
-
diagnostic: `ref resolves to ${existing}, not expected object ${objectId}`,
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
const git = createGit(cwd);
|
|
93
|
-
await git.raw(["update-ref", "-d", ref, objectId]);
|
|
94
|
-
return { ref, status: "removed" };
|
|
178
|
+
result = await (options.run ?? runGitProcess)(cwd, ["update-ref", "--no-deref", "--stdin"], { input });
|
|
95
179
|
}
|
|
96
180
|
catch (error) {
|
|
181
|
+
result = {
|
|
182
|
+
stdout: "",
|
|
183
|
+
stderr: "",
|
|
184
|
+
status: null,
|
|
185
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
if (result.status === 0 && !result.error) {
|
|
189
|
+
return { status: "committed", proof: "process-success", operations, observations: [] };
|
|
190
|
+
}
|
|
191
|
+
const observations = await observeTransactionRefs(cwd, operations);
|
|
192
|
+
const allPre = observations.every((observation) => observationMatches(observation, observation.expectedOldOid));
|
|
193
|
+
const allPost = observations.every((observation) => observationMatches(observation, observation.requestedNewOid));
|
|
194
|
+
const changesAuthority = operations.some((operation) => requestedNewOid(operation) !== operation.expectedOldOid);
|
|
195
|
+
const cause = transactionFailureCause(result);
|
|
196
|
+
if (allPost && changesAuthority) {
|
|
197
|
+
return { status: "committed", proof: "observed-post-state", operations, observations };
|
|
198
|
+
}
|
|
199
|
+
if (isUnambiguousCasRefusal(result)
|
|
200
|
+
|| (isUnableToResolveReference(result) && hasConclusiveMissingExpectedReference(observations))) {
|
|
201
|
+
return {
|
|
202
|
+
status: "refused",
|
|
203
|
+
code: REF_TRANSACTION_CAS_MISMATCH,
|
|
204
|
+
operations,
|
|
205
|
+
observations,
|
|
206
|
+
cause,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
if (allPre && changesAuthority) {
|
|
97
210
|
return {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
211
|
+
status: "refused",
|
|
212
|
+
code: REF_TRANSACTION_NOT_COMMITTED,
|
|
213
|
+
operations,
|
|
214
|
+
observations,
|
|
215
|
+
cause,
|
|
101
216
|
};
|
|
102
217
|
}
|
|
218
|
+
return {
|
|
219
|
+
status: "indeterminate",
|
|
220
|
+
code: REF_TRANSACTION_INDETERMINATE,
|
|
221
|
+
operations,
|
|
222
|
+
observations,
|
|
223
|
+
cause,
|
|
224
|
+
};
|
|
103
225
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
+
import { isFullGitObjectId } from "./core.js";
|
|
2
3
|
import { normalizeFilesystemCoordinate } from "../fs/path-coordinate.js";
|
|
3
|
-
import { gitProcessEnv, resolveGitTimeoutMs, } from "./core.js";
|
|
4
|
+
import { gitProcessEnv, resolveGitTimeoutMs, terminateGitChild, } from "./core.js";
|
|
4
5
|
/** Default long-lived batch argv. One-shot calls must not use this gate. */
|
|
5
6
|
export const GIT_STREAMING_BATCH_DEFAULT_ARGS = ["cat-file", "--batch"];
|
|
6
7
|
/** Safety cap for a single response header line before the body frame. */
|
|
@@ -15,10 +16,12 @@ export class GitStreamingBatchResponseEndedError extends Error {
|
|
|
15
16
|
}
|
|
16
17
|
class BufferedReader {
|
|
17
18
|
#iterator;
|
|
19
|
+
#onProgress;
|
|
18
20
|
#buffer = Buffer.alloc(0);
|
|
19
21
|
#ended = false;
|
|
20
|
-
constructor(stream) {
|
|
22
|
+
constructor(stream, onProgress) {
|
|
21
23
|
this.#iterator = stream[Symbol.asyncIterator]();
|
|
24
|
+
this.#onProgress = onProgress;
|
|
22
25
|
}
|
|
23
26
|
async #fill() {
|
|
24
27
|
if (this.#ended) {
|
|
@@ -30,6 +33,7 @@ class BufferedReader {
|
|
|
30
33
|
throw new GitStreamingBatchResponseEndedError("git streaming batch response ended unexpectedly");
|
|
31
34
|
}
|
|
32
35
|
const chunk = Buffer.isBuffer(next.value) ? next.value : Buffer.from(next.value);
|
|
36
|
+
this.#onProgress();
|
|
33
37
|
this.#buffer = this.#buffer.length === 0 ? chunk : Buffer.concat([this.#buffer, chunk]);
|
|
34
38
|
}
|
|
35
39
|
async readLine(maxBytes) {
|
|
@@ -84,20 +88,36 @@ export class GitStreamingBatchSession {
|
|
|
84
88
|
#child;
|
|
85
89
|
#reader;
|
|
86
90
|
#stderr = [];
|
|
87
|
-
#
|
|
91
|
+
#timeoutMs;
|
|
88
92
|
#closed;
|
|
89
93
|
#maxHeaderBytes;
|
|
94
|
+
#signal;
|
|
95
|
+
#abortListener;
|
|
96
|
+
#timeout = null;
|
|
90
97
|
#state = { kind: "open" };
|
|
91
98
|
#timedOut = false;
|
|
92
99
|
#aborted = false;
|
|
93
100
|
#inputError = null;
|
|
94
101
|
#requestInFlight = false;
|
|
102
|
+
#requestDone = null;
|
|
103
|
+
#finishRequest = null;
|
|
104
|
+
#requestTimeoutOwner = null;
|
|
95
105
|
#closePromise = null;
|
|
96
|
-
|
|
106
|
+
#terminationPromise = null;
|
|
107
|
+
constructor(cwd, child, timeoutMs, maxHeaderBytes, signal) {
|
|
97
108
|
this.cwd = cwd;
|
|
98
109
|
this.#child = child;
|
|
99
|
-
this.#
|
|
110
|
+
this.#timeoutMs = timeoutMs;
|
|
111
|
+
this.#reader = new BufferedReader(this.#child.stdout, () => {
|
|
112
|
+
if (this.#requestTimeoutOwner)
|
|
113
|
+
this.#armTimeout(this.#requestTimeoutOwner);
|
|
114
|
+
});
|
|
100
115
|
this.#maxHeaderBytes = maxHeaderBytes;
|
|
116
|
+
this.#signal = signal;
|
|
117
|
+
this.#abortListener = () => this.abort();
|
|
118
|
+
signal?.addEventListener("abort", this.#abortListener, { once: true });
|
|
119
|
+
if (signal?.aborted)
|
|
120
|
+
this.abort();
|
|
101
121
|
this.#child.stderr.on("data", (chunk) => this.#stderr.push(chunk));
|
|
102
122
|
this.#child.stdin.on("error", (error) => {
|
|
103
123
|
this.#inputError = error;
|
|
@@ -110,10 +130,27 @@ export class GitStreamingBatchSession {
|
|
|
110
130
|
resolve({ code, signal });
|
|
111
131
|
});
|
|
112
132
|
});
|
|
113
|
-
|
|
133
|
+
}
|
|
134
|
+
#armTimeout(owner) {
|
|
135
|
+
this.#clearTimeout();
|
|
136
|
+
const handle = setTimeout(() => {
|
|
137
|
+
if (this.#timeout?.owner !== owner)
|
|
138
|
+
return;
|
|
139
|
+
this.#timeout = null;
|
|
114
140
|
this.#timedOut = true;
|
|
141
|
+
if (this.#state.kind === "open")
|
|
142
|
+
this.#state = { kind: "closing" };
|
|
115
143
|
this.#child.kill("SIGKILL");
|
|
116
|
-
}, timeoutMs);
|
|
144
|
+
}, this.#timeoutMs);
|
|
145
|
+
this.#timeout = { owner, handle };
|
|
146
|
+
}
|
|
147
|
+
#clearTimeout(owner) {
|
|
148
|
+
if (this.#timeout === null)
|
|
149
|
+
return;
|
|
150
|
+
if (owner !== undefined && this.#timeout.owner !== owner)
|
|
151
|
+
return;
|
|
152
|
+
clearTimeout(this.#timeout.handle);
|
|
153
|
+
this.#timeout = null;
|
|
117
154
|
}
|
|
118
155
|
/**
|
|
119
156
|
* Open the sole long-lived Git batch process for `cwd`.
|
|
@@ -125,11 +162,11 @@ export class GitStreamingBatchSession {
|
|
|
125
162
|
const maxHeaderBytes = options?.maxHeaderBytes ?? GIT_STREAMING_BATCH_MAX_HEADER_BYTES;
|
|
126
163
|
const child = spawn("git", [...args], {
|
|
127
164
|
cwd: normalizeFilesystemCoordinate(cwd),
|
|
128
|
-
env: gitProcessEnv(),
|
|
165
|
+
env: gitProcessEnv(options?.env),
|
|
129
166
|
shell: false,
|
|
130
167
|
stdio: ["pipe", "pipe", "pipe"],
|
|
131
168
|
});
|
|
132
|
-
return new GitStreamingBatchSession(cwd, child, timeoutMs, maxHeaderBytes);
|
|
169
|
+
return new GitStreamingBatchSession(cwd, child, timeoutMs, maxHeaderBytes, options?.signal);
|
|
133
170
|
}
|
|
134
171
|
/**
|
|
135
172
|
* Write one request line and read one framed response record.
|
|
@@ -142,7 +179,13 @@ export class GitStreamingBatchSession {
|
|
|
142
179
|
if (this.#requestInFlight) {
|
|
143
180
|
throw new Error("git streaming batch rejects concurrent requests");
|
|
144
181
|
}
|
|
182
|
+
const timeoutOwner = Symbol("git-streaming-batch-request");
|
|
145
183
|
this.#requestInFlight = true;
|
|
184
|
+
this.#requestTimeoutOwner = timeoutOwner;
|
|
185
|
+
this.#requestDone = new Promise((resolve) => {
|
|
186
|
+
this.#finishRequest = resolve;
|
|
187
|
+
});
|
|
188
|
+
this.#armTimeout(timeoutOwner);
|
|
146
189
|
try {
|
|
147
190
|
if (this.#inputError)
|
|
148
191
|
throw this.#inputError;
|
|
@@ -168,7 +211,8 @@ export class GitStreamingBatchSession {
|
|
|
168
211
|
catch (error) {
|
|
169
212
|
if (error instanceof GitStreamingBatchFramingError
|
|
170
213
|
|| error instanceof GitStreamingBatchResponseEndedError) {
|
|
171
|
-
this
|
|
214
|
+
if (!this.#timedOut)
|
|
215
|
+
this.abort();
|
|
172
216
|
}
|
|
173
217
|
throw error;
|
|
174
218
|
}
|
|
@@ -183,7 +227,7 @@ export class GitStreamingBatchSession {
|
|
|
183
227
|
const size = Number(sizeText);
|
|
184
228
|
if (unexpected.length > 0
|
|
185
229
|
|| !oid
|
|
186
|
-
||
|
|
230
|
+
|| !isFullGitObjectId(oid)
|
|
187
231
|
|| !type
|
|
188
232
|
|| !Number.isSafeInteger(size)
|
|
189
233
|
|| size < 0) {
|
|
@@ -221,7 +265,13 @@ export class GitStreamingBatchSession {
|
|
|
221
265
|
};
|
|
222
266
|
}
|
|
223
267
|
finally {
|
|
268
|
+
this.#clearTimeout(timeoutOwner);
|
|
269
|
+
this.#requestTimeoutOwner = null;
|
|
224
270
|
this.#requestInFlight = false;
|
|
271
|
+
const finishRequest = this.#finishRequest;
|
|
272
|
+
this.#finishRequest = null;
|
|
273
|
+
this.#requestDone = null;
|
|
274
|
+
finishRequest?.();
|
|
225
275
|
}
|
|
226
276
|
}
|
|
227
277
|
/**
|
|
@@ -232,11 +282,12 @@ export class GitStreamingBatchSession {
|
|
|
232
282
|
if (this.#aborted)
|
|
233
283
|
return;
|
|
234
284
|
this.#aborted = true;
|
|
285
|
+
this.#clearTimeout();
|
|
235
286
|
if (this.#state.kind === "open") {
|
|
236
287
|
this.#state = { kind: "closing" };
|
|
237
288
|
}
|
|
238
289
|
this.#child.stdin.destroy();
|
|
239
|
-
this.#child
|
|
290
|
+
this.#terminationPromise ??= terminateGitChild(this.#child, this.#closed);
|
|
240
291
|
}
|
|
241
292
|
/**
|
|
242
293
|
* End stdin, await process exit, and return structured close facts.
|
|
@@ -263,7 +314,15 @@ export class GitStreamingBatchSession {
|
|
|
263
314
|
return this.#closePromise;
|
|
264
315
|
}
|
|
265
316
|
async #closeOnce() {
|
|
317
|
+
const pendingRequest = this.#requestDone;
|
|
318
|
+
if (pendingRequest)
|
|
319
|
+
await pendingRequest;
|
|
320
|
+
if (this.#terminationPromise)
|
|
321
|
+
await this.#terminationPromise;
|
|
322
|
+
const timeoutOwner = Symbol("git-streaming-batch-close");
|
|
266
323
|
if (!this.#aborted && this.#child.stdin?.writable) {
|
|
324
|
+
if (!this.#timedOut)
|
|
325
|
+
this.#armTimeout(timeoutOwner);
|
|
267
326
|
this.#child.stdin.end();
|
|
268
327
|
}
|
|
269
328
|
let exit;
|
|
@@ -271,8 +330,9 @@ export class GitStreamingBatchSession {
|
|
|
271
330
|
exit = await this.#closed;
|
|
272
331
|
}
|
|
273
332
|
finally {
|
|
274
|
-
clearTimeout(
|
|
333
|
+
this.#clearTimeout(timeoutOwner);
|
|
275
334
|
}
|
|
335
|
+
this.#signal?.removeEventListener("abort", this.#abortListener);
|
|
276
336
|
if (exit.processError && !this.#aborted) {
|
|
277
337
|
const fact = {
|
|
278
338
|
code: null,
|
package/package.json
CHANGED
package/skills/keiyaku/SKILL.md
CHANGED
|
@@ -33,7 +33,7 @@ EOF
|
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
That's the whole loop. Skip the task when the work needs no planning:
|
|
36
|
-
`keiyaku bind --
|
|
36
|
+
`keiyaku bind --title TEXT --context TEXT --objective TEXT --design TEXT --scope GLOB --criteria TEXT [--verification COMMAND]` (or the same settled document through `keiyaku bind -` — `bind --help` shows the shape).
|
|
37
37
|
|
|
38
38
|
## Getting oriented
|
|
39
39
|
|
|
@@ -41,10 +41,10 @@ addressable. `revive` starts a new projection and does not inherit an alias.
|
|
|
41
41
|
|
|
42
42
|
```bash
|
|
43
43
|
keiyaku akuma ls # who's callable
|
|
44
|
-
keiyaku akuma
|
|
44
|
+
keiyaku akuma show NAME # resolved profile + live projections
|
|
45
45
|
keiyaku call NAME "do the thing" # run in foreground until it finishes
|
|
46
46
|
keiyaku call NAME --wait 10m "do the thing" # bounded: after 10m you get a snapshot, it keeps running
|
|
47
|
-
keiyaku call NAME
|
|
47
|
+
keiyaku call NAME -d "do the thing" # short for --detach; note the projection id
|
|
48
48
|
keiyaku call NAME - < prompt.md # long prompt: literal `-` reads stdin
|
|
49
49
|
keiyaku wait <proj-id> # join a running one; prints its activity
|
|
50
50
|
keiyaku wait <proj-id> --timeout 5m # peek for 5m; timeout = snapshot, not death
|
|
@@ -63,7 +63,7 @@ keiyaku status # forgot an id? it's on the board
|
|
|
63
63
|
<KEIYAKU_HOME>/akuma/<name>.md user profile (normally ~/.keiyaku)
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
Use `keiyaku akuma
|
|
66
|
+
Use `keiyaku akuma show NAME` to inspect the resolved source and configuration.
|
|
67
67
|
|
|
68
68
|
## Expectations
|
|
69
69
|
|
|
@@ -52,8 +52,10 @@ keiyaku bind --task fix-parser --task fix-parser-help - < contract.md
|
|
|
52
52
|
Only ready tasks bind. Multi-bind only when the tasks describe one delivery
|
|
53
53
|
intent, share a coherent write surface, and should settle together; otherwise
|
|
54
54
|
keep separate contracts and express order with task `needs` or contract
|
|
55
|
-
`--after`. The first task supplies
|
|
56
|
-
bind document for
|
|
55
|
+
`--after`. The first task supplies title/objective defaults; provide a partial
|
|
56
|
+
bind document for every remaining resolved section. Final bind admission always
|
|
57
|
+
requires Title, Context, Objective, Design, Scope, and Criteria; Verification
|
|
58
|
+
is optional.
|
|
57
59
|
|
|
58
60
|
## Gotcha
|
|
59
61
|
|