@astrosheep/keiyaku 4.0.6 → 4.1.0
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/src/akuma/archetype.js +8 -1
- package/build/src/akuma/provider-recipe.d.ts +2 -0
- package/build/src/akuma/provider-recipe.js +10 -0
- package/build/src/akuma/providers/acp/config.d.ts +2 -0
- package/build/src/akuma/providers/acp/config.js +9 -1
- package/build/src/akuma/providers/acp/core.d.ts +5 -2
- package/build/src/akuma/providers/acp/core.js +29 -16
- package/build/src/akuma/providers/acp/index.js +8 -2
- package/build/src/akuma/providers/claude/index.d.ts +1 -1
- package/build/src/akuma/providers/claude/index.js +9 -5
- package/build/src/akuma/providers/codex-app-server/index.js +4 -1
- package/build/src/akuma/providers/grok-build/index.js +15 -1
- package/build/src/akuma/providers/index.js +1 -1
- package/build/src/akuma/providers/opencode-sdk/index.js +3 -0
- package/build/src/akuma/providers/pi/index.js +3 -1
- package/build/src/body/amend.js +0 -9
- package/build/src/body/region.js +2 -2
- package/build/src/cli/accepted.js +2 -0
- package/build/src/cli/commands/contract.d.ts +5 -3
- package/build/src/cli/commands/contract.js +23 -13
- package/build/src/cli/commands/task-invoke.d.ts +4 -1
- package/build/src/cli/commands/task-invoke.js +6 -3
- package/build/src/cli/commands/task.js +10 -2
- package/build/src/cli/parse.js +2 -2
- package/build/src/cli/render/akuma.js +42 -11
- package/build/src/cli/render/catalog.js +5 -1
- package/build/src/cli/render/contract.js +25 -3
- package/build/src/cli/render/kanshi.js +142 -94
- package/build/src/cli/render/receipt.d.ts +1 -0
- package/build/src/cli/render/receipt.js +18 -14
- package/build/src/cli/render/refusal.js +6 -0
- package/build/src/cli/render/task.js +63 -11
- package/build/src/cli/result.d.ts +8 -1
- package/build/src/contract-worktree.d.ts +0 -2
- package/build/src/contract-worktree.js +13 -24
- package/build/src/git/nuke.js +1 -6
- package/build/src/git/reconcile.d.ts +0 -2
- package/build/src/git/reconcile.js +2 -33
- package/build/src/git/tender.d.ts +13 -2
- package/build/src/git/tender.js +43 -4
- package/build/src/kanshi/read.js +32 -12
- package/build/src/kanshi/report.d.ts +2 -1
- package/build/src/library/continuation.d.ts +26 -0
- package/build/src/library/continuation.js +78 -0
- package/build/src/library/contract.d.ts +9 -5
- package/build/src/library/contract.js +27 -2
- package/build/src/library/delivery.d.ts +18 -13
- package/build/src/library/keiyaku.d.ts +1 -1
- package/build/src/library/mutation.js +11 -4
- package/build/src/protocol/deliver.d.ts +12 -2
- package/build/src/protocol/deliver.js +52 -19
- package/build/src/protocol/operations.d.ts +7 -2
- package/build/src/settlement/fence.d.ts +0 -2
- package/build/src/settlement/fence.js +0 -24
- package/build/src/task/board.d.ts +21 -2
- package/build/src/task/board.js +25 -12
- package/build/src/task/identity.js +14 -4
- package/build/src/task/index.d.ts +1 -0
- package/build/src/task/index.js +7 -1
- package/build/src/task/operations.d.ts +2 -1
- package/build/src/task/operations.js +15 -4
- package/build/src/task/query.js +2 -2
- package/build/src/task/store.d.ts +2 -0
- package/build/src/task/store.js +12 -83
- package/package.json +1 -1
|
@@ -25,6 +25,7 @@ import { reconcileInput, scopeForRepo } from "./repo.js";
|
|
|
25
25
|
import { injectedBodyRequests, requestBodyDeliver, requestBodyReview, } from "../akuma/requests.js";
|
|
26
26
|
import { auditContract } from "./audit.js";
|
|
27
27
|
import { Delivery, deliveryHandle } from "./delivery.js";
|
|
28
|
+
import { continueDeliveredDependents } from "./continuation.js";
|
|
28
29
|
import { completionInput, completeHolderMutation, completeMutation, } from "./mutation.js";
|
|
29
30
|
import { completeReconcile } from "./reconcile.js";
|
|
30
31
|
import { admitBindWithAppointment } from "./bind.js";
|
|
@@ -75,9 +76,22 @@ async function executeLocalDelivery(input) {
|
|
|
75
76
|
});
|
|
76
77
|
if (outcome.kind === "integration-conflict-materialized")
|
|
77
78
|
return outcome;
|
|
79
|
+
const accepted = requireAccepted(outcome);
|
|
80
|
+
const continued = accepted.value.completion === undefined
|
|
81
|
+
? accepted
|
|
82
|
+
: await continueDeliveredDependents({
|
|
83
|
+
scope: input.scope,
|
|
84
|
+
channel,
|
|
85
|
+
contractId: input.contractId,
|
|
86
|
+
accepted,
|
|
87
|
+
deriveDocument: derivedDocument,
|
|
88
|
+
resolveHereWorkspace: async (id) => await resolveHereWorkspace(input.scope, id),
|
|
89
|
+
...(input.actor === undefined ? {} : { actor: input.actor }),
|
|
90
|
+
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
91
|
+
});
|
|
78
92
|
return completeMutation({
|
|
79
93
|
...completionInput(input.scope, channel, input.contractId, (delivery) => delivery, input.hooks),
|
|
80
|
-
accepted:
|
|
94
|
+
accepted: continued,
|
|
81
95
|
});
|
|
82
96
|
});
|
|
83
97
|
}
|
|
@@ -93,9 +107,20 @@ async function executeLocalReview(input) {
|
|
|
93
107
|
...(input.actor === undefined ? {} : { actor: input.actor }),
|
|
94
108
|
resolveHereWorkspace: async (id) => await resolveHereWorkspace(input.scope, id),
|
|
95
109
|
}));
|
|
110
|
+
const continued = accepted.value.completion === undefined
|
|
111
|
+
? accepted
|
|
112
|
+
: await continueDeliveredDependents({
|
|
113
|
+
scope: input.scope,
|
|
114
|
+
channel,
|
|
115
|
+
contractId: input.contractId,
|
|
116
|
+
accepted,
|
|
117
|
+
deriveDocument: derivedDocument,
|
|
118
|
+
resolveHereWorkspace: async (id) => await resolveHereWorkspace(input.scope, id),
|
|
119
|
+
...(input.actor === undefined ? {} : { actor: input.actor }),
|
|
120
|
+
});
|
|
96
121
|
return completeMutation({
|
|
97
122
|
...completionInput(input.scope, channel, input.contractId, (review) => review, input.hooks),
|
|
98
|
-
accepted,
|
|
123
|
+
accepted: continued,
|
|
99
124
|
});
|
|
100
125
|
});
|
|
101
126
|
}
|
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
import type { SnapshotId } from "../core/facts/types.js";
|
|
2
|
-
import type { DeliverValue } from "../protocol/deliver.js";
|
|
2
|
+
import type { DeliverValue as ProtocolDeliverValue } from "../protocol/deliver.js";
|
|
3
|
+
import type { ContinuationReport } from "./continuation.js";
|
|
4
|
+
export type DeliveryValue = ProtocolDeliverValue & Readonly<{
|
|
5
|
+
continuation?: ContinuationReport;
|
|
6
|
+
}>;
|
|
3
7
|
declare class DeliveryHandle {
|
|
4
8
|
private readonly readDiff;
|
|
5
|
-
readonly completion?:
|
|
6
|
-
readonly verification?:
|
|
7
|
-
readonly verificationReuse?:
|
|
8
|
-
readonly verificationSummary?:
|
|
9
|
-
readonly placement?:
|
|
10
|
-
readonly cleanup?:
|
|
11
|
-
readonly leak?:
|
|
9
|
+
readonly completion?: DeliveryValue["completion"];
|
|
10
|
+
readonly verification?: DeliveryValue["verification"];
|
|
11
|
+
readonly verificationReuse?: DeliveryValue["verificationReuse"];
|
|
12
|
+
readonly verificationSummary?: DeliveryValue["verificationSummary"];
|
|
13
|
+
readonly placement?: DeliveryValue["placement"];
|
|
14
|
+
readonly cleanup?: DeliveryValue["cleanup"];
|
|
15
|
+
readonly leak?: DeliveryValue["leak"];
|
|
16
|
+
readonly continuation?: DeliveryValue["continuation"];
|
|
12
17
|
readonly tenderSnapshot: SnapshotId;
|
|
13
|
-
readonly integration:
|
|
14
|
-
readonly method:
|
|
15
|
-
readonly policy:
|
|
16
|
-
constructor(identity: Pick<
|
|
18
|
+
readonly integration: DeliveryValue["integration"];
|
|
19
|
+
readonly method: DeliveryValue["method"];
|
|
20
|
+
readonly policy: DeliveryValue["policy"];
|
|
21
|
+
constructor(identity: Pick<DeliveryValue, "tenderSnapshot" | "integration" | "method" | "policy">, readDiff: () => Promise<string | null>, outcomes?: Partial<Pick<DeliveryValue, "completion" | "verification" | "verificationReuse" | "verificationSummary" | "placement" | "cleanup" | "leak" | "continuation">>);
|
|
17
22
|
diff(): Promise<string | null>;
|
|
18
23
|
}
|
|
19
24
|
export type Delivery = DeliveryHandle;
|
|
@@ -22,5 +27,5 @@ type HandleType<T extends object> = Readonly<{
|
|
|
22
27
|
[Symbol.hasInstance](value: unknown): boolean;
|
|
23
28
|
}>;
|
|
24
29
|
export declare const Delivery: HandleType<DeliveryHandle>;
|
|
25
|
-
export declare function deliveryHandle(delivery:
|
|
30
|
+
export declare function deliveryHandle(delivery: DeliveryValue, readDiff: () => Promise<string | null>): Delivery;
|
|
26
31
|
export {};
|
|
@@ -8,7 +8,7 @@ export { Delivery, KeiyakuRefused, KeiyakuRetry, } from "./contract.js";
|
|
|
8
8
|
export { NoGitWorldError, Repo } from "./repo.js";
|
|
9
9
|
export { gatesFrom, requireBranchesToBeUpToDateFrom, SettingsError, worktreeHooksFrom, } from "./configuration.js";
|
|
10
10
|
export { AkumaWorldScopeError } from "./address.js";
|
|
11
|
-
export type { AbandonInput, ActorId, AmendInput, AmendResult, ArcInput, AttestationVerdict, AuditInput, AuditReport, DeliveryPreparationRefusal, BindInput, BindResult, ContractBoard, ContractDisposition, ContractGateCurrent, ContractGateReport, ContractHistory, ContractHistoryEvent, ContractListInput, ContractObservation, ContractObservationInput, ContractPhase, ContractRow, DeliverInput, Fact, IntegrationConflictMaterialized, FactKind, KeiyakuOfInput, KeiyakuRefusal, KeiyakuRetryReason, Lag, MutationResult, PlacementStop, ReconcileReport, Review, ReviewInput, TaskId, TopologyEffect, VerificationReuse, VerificationStop, } from "./contract.js";
|
|
11
|
+
export type { AbandonInput, ActorId, AmendInput, AmendResult, ArcInput, AttestationVerdict, AuditInput, AuditReport, DeliveryPreparationRefusal, BindInput, BindResult, ContractBoard, ContractDisposition, ContractGateCurrent, ContractGateReport, ContractHistory, ContractHistoryEvent, ContractListInput, ContractObservation, ContractObservationInput, ContractPhase, ContractRow, ContinuationReport, DeliverInput, Fact, IntegrationConflictMaterialized, FactKind, KeiyakuOfInput, KeiyakuRefusal, KeiyakuRetryReason, Lag, MutationResult, PlacementStop, ReconcileReport, Review, ReviewInput, TaskId, TopologyEffect, VerificationReuse, VerificationStop, } from "./contract.js";
|
|
12
12
|
export type { ContractId, ContractState, ChangeId, SnapshotId, } from "../core/facts/types.js";
|
|
13
13
|
export type { Gate, GatesFromInput, HookCommand, RequireBranchesToBeUpToDateFromInput, WorktreeHooks, } from "./configuration.js";
|
|
14
14
|
export type { RegionOverlap } from "./region.js";
|
|
@@ -5,7 +5,11 @@ export function completionInput(scope, channel, contractId, value, hooks) {
|
|
|
5
5
|
}
|
|
6
6
|
export async function completeMutation(input) {
|
|
7
7
|
const { scope, channel, contractId, accepted, value, hooks } = input;
|
|
8
|
-
const
|
|
8
|
+
const contracts = [...new Set([contractId, ...accepted.facts.map((fact) => fact.contract)])];
|
|
9
|
+
const reports = [];
|
|
10
|
+
for (const affected of contracts) {
|
|
11
|
+
reports.push(await completeReconcile({ scope, channel, contractId: affected, hooks, retryHooks: false }));
|
|
12
|
+
}
|
|
9
13
|
const obligations = {
|
|
10
14
|
...(accepted.cleanup === undefined ? {} : { cleanup: accepted.cleanup }),
|
|
11
15
|
...(accepted.leak === undefined ? {} : { leak: accepted.leak }),
|
|
@@ -14,9 +18,12 @@ export async function completeMutation(input) {
|
|
|
14
18
|
facts: accepted.facts,
|
|
15
19
|
head: accepted.head,
|
|
16
20
|
value: value(accepted.value),
|
|
17
|
-
effects: [...(accepted.physical?.effects ?? []), ...report.effects],
|
|
18
|
-
lags: [...(accepted.physical?.lag ?? []), ...report.lag],
|
|
19
|
-
settlement:
|
|
21
|
+
effects: [...(accepted.physical?.effects ?? []), ...reports.flatMap((report) => report.effects)],
|
|
22
|
+
lags: [...(accepted.physical?.lag ?? []), ...reports.flatMap((report) => report.lag)],
|
|
23
|
+
settlement: {
|
|
24
|
+
actions: reports.flatMap((report) => report.settlement.actions),
|
|
25
|
+
lags: reports.flatMap((report) => report.settlement.lags),
|
|
26
|
+
},
|
|
20
27
|
...obligations,
|
|
21
28
|
};
|
|
22
29
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { ActorId, ContractId, ContractState, DeliverData, SnapshotId } from "../core/facts/types.js";
|
|
1
|
+
import type { ActorId, ContractId, ContractState, DeliverData, JournalEntry, SnapshotId } from "../core/facts/types.js";
|
|
2
2
|
import type { CurrentVerifiedAttestation } from "./intent.js";
|
|
3
|
-
import { type CompletionEvidence } from "./completion.js";
|
|
3
|
+
import { type CompletionEvidence, type CompletionResult } from "./completion.js";
|
|
4
4
|
import { type ManagedWorktreeAppointment } from "../workspace-place.js";
|
|
5
5
|
import type { DeliveryPreparationRefusal, DocumentDerivation, IntentOutcome, MutationOperationInput } from "./operations.js";
|
|
6
6
|
type DeliveryIdentity = DeliverData;
|
|
@@ -45,5 +45,15 @@ export declare function prepareDelivery(repository: import("../git/process.js").
|
|
|
45
45
|
kind: "refused";
|
|
46
46
|
refusal: DeliveryPreparationRefusal;
|
|
47
47
|
}>;
|
|
48
|
+
export declare function continueDeliveryOperation(input: Readonly<{
|
|
49
|
+
scope: MutationOperationInput["scope"];
|
|
50
|
+
channel: MutationOperationInput["channel"];
|
|
51
|
+
state: ContractState;
|
|
52
|
+
journal: readonly JournalEntry[];
|
|
53
|
+
deriveDocument: (state: ContractState) => DocumentDerivation;
|
|
54
|
+
resolveHereWorkspace?: MutationOperationInput["resolveHereWorkspace"];
|
|
55
|
+
actor?: ActorId;
|
|
56
|
+
signal?: AbortSignal;
|
|
57
|
+
}>): Promise<CompletionResult>;
|
|
48
58
|
export declare function deliverOperation(input: DeliverOperationInput): Promise<IntentOutcome<DeliverValue> | IntegrationConflictMaterialized>;
|
|
49
59
|
export {};
|
|
@@ -34,10 +34,12 @@ export async function prepareDelivery(repository, stage, input) {
|
|
|
34
34
|
coordinates,
|
|
35
35
|
...(appointed === undefined ? {} : { place: appointed.place }),
|
|
36
36
|
...(stage.workspacePath === undefined ? {} : { workspacePath: stage.workspacePath }),
|
|
37
|
+
captureMergeState: true,
|
|
38
|
+
rejectUnmerged: true,
|
|
37
39
|
});
|
|
38
40
|
if (tender.kind === "refused")
|
|
39
41
|
return tender;
|
|
40
|
-
if ((tender.data.dirty && input.includeDirty !== true) || tender.data.changes.submodules.length > 0) {
|
|
42
|
+
if (((tender.data.dirty || tender.data.mergeHead !== undefined) && input.includeDirty !== true) || tender.data.changes.submodules.length > 0) {
|
|
41
43
|
return { kind: "refused", refusal: await dirtyTenderRefusal(repository, contractId, tender.data) };
|
|
42
44
|
}
|
|
43
45
|
const commit = await prepareDeliveryCommitMetadata(repository, {
|
|
@@ -86,18 +88,23 @@ async function deliverAttempt(input, attempt) {
|
|
|
86
88
|
workspacePath = state.terminal === null && state.coordinates.workspace === "here"
|
|
87
89
|
? await input.resolveHereWorkspace?.(state.id)
|
|
88
90
|
: undefined;
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
91
|
+
const materialization = input.materializeConflict === true && state.terminal === null
|
|
92
|
+
? await materializationMergeStateRefusal(input)
|
|
93
|
+
: undefined;
|
|
94
|
+
const prepared = materialization === undefined
|
|
95
|
+
? await prepareDelivery(input.scope, {
|
|
96
|
+
contractId: state.id,
|
|
97
|
+
coordinates: state.coordinates,
|
|
98
|
+
...(workspacePath === undefined ? {} : { workspacePath }),
|
|
99
|
+
}, {
|
|
100
|
+
title: derivation.title,
|
|
101
|
+
document: derivation.bytes,
|
|
102
|
+
...(input.actor === undefined ? {} : { actor: input.actor }),
|
|
103
|
+
...(input.message === undefined ? {} : { message: input.message }),
|
|
104
|
+
requireBranchesToBeUpToDate: input.requireBranchesToBeUpToDate,
|
|
105
|
+
includeDirty: input.includeDirty,
|
|
106
|
+
})
|
|
107
|
+
: { kind: "refused", refusal: materialization };
|
|
101
108
|
preparation = prepared.kind === "refused"
|
|
102
109
|
? { kind: "refused", document: derivation.document, refusal: prepared.refusal }
|
|
103
110
|
: {
|
|
@@ -165,6 +172,24 @@ async function completeDelivery(input, first) {
|
|
|
165
172
|
...completed.evidence,
|
|
166
173
|
});
|
|
167
174
|
}
|
|
175
|
+
export async function continueDeliveryOperation(input) {
|
|
176
|
+
const contractId = input.state.id;
|
|
177
|
+
const hereWorkspacePath = input.state.coordinates.workspace === "here"
|
|
178
|
+
? await input.resolveHereWorkspace?.(contractId)
|
|
179
|
+
: undefined;
|
|
180
|
+
return await completeCandidate({
|
|
181
|
+
channel: input.channel,
|
|
182
|
+
repository: input.scope,
|
|
183
|
+
contractId,
|
|
184
|
+
...(input.actor === undefined ? {} : { actor: input.actor }),
|
|
185
|
+
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
186
|
+
...(input.state.coordinates.target === undefined ? {} : { target: input.state.coordinates.target }),
|
|
187
|
+
verification: input.deriveDocument(input.state).verification,
|
|
188
|
+
initial: { kind: "accepted", state: input.state, journal: input.journal, facts: [] },
|
|
189
|
+
verifyInitial: true,
|
|
190
|
+
...(hereWorkspacePath === undefined ? {} : { hereWorkspacePath }),
|
|
191
|
+
});
|
|
192
|
+
}
|
|
168
193
|
function isIntegrationConflict(refusal) {
|
|
169
194
|
return refusal.kind === "integration-failed" && refusal.reason === "conflict";
|
|
170
195
|
}
|
|
@@ -201,6 +226,17 @@ async function appointedDeliverWorkspace(input) {
|
|
|
201
226
|
coordinates: state.coordinates,
|
|
202
227
|
};
|
|
203
228
|
}
|
|
229
|
+
async function materializationMergeStateRefusal(input) {
|
|
230
|
+
const appointed = await appointedDeliverWorkspace(input);
|
|
231
|
+
if ("kind" in appointed)
|
|
232
|
+
return appointed.refusal;
|
|
233
|
+
return await mergeStatePresentRefusal(input, appointed.workspace);
|
|
234
|
+
}
|
|
235
|
+
async function mergeStatePresentRefusal(input, workspace) {
|
|
236
|
+
if (!(await workspaceMergeStatePresent(input.scope, workspace.path)))
|
|
237
|
+
return undefined;
|
|
238
|
+
return { kind: "merge-state-present", contractId: input.contractId, workspace };
|
|
239
|
+
}
|
|
204
240
|
async function materializeDeliverConflict(input, refusal) {
|
|
205
241
|
if (refusal.conflictPaths === undefined)
|
|
206
242
|
throw new Error("conflicted integration is missing conflict paths");
|
|
@@ -208,12 +244,9 @@ async function materializeDeliverConflict(input, refusal) {
|
|
|
208
244
|
if ("kind" in appointed)
|
|
209
245
|
return appointed;
|
|
210
246
|
const { workspace, coordinates } = appointed;
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
refusal: { kind: "merge-state-present", contractId: input.contractId, workspace },
|
|
215
|
-
};
|
|
216
|
-
}
|
|
247
|
+
const mergeState = await mergeStatePresentRefusal(input, workspace);
|
|
248
|
+
if (mergeState !== undefined)
|
|
249
|
+
return { kind: "refused", refusal: mergeState };
|
|
217
250
|
const tender = await captureTender(input.scope, {
|
|
218
251
|
contractId: input.contractId,
|
|
219
252
|
coordinates,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { DirtyWorkspaceRefusal } from "../git/tender.js";
|
|
2
2
|
import type { GitRepository } from "../git/process.js";
|
|
3
3
|
import { type GitDecodeChannel } from "../git/read-observation.js";
|
|
4
4
|
import type { WorktreeLeak } from "../git/scratch.js";
|
|
@@ -28,6 +28,11 @@ export type MergeStatePresentRefusal = Readonly<{
|
|
|
28
28
|
path: string;
|
|
29
29
|
}>;
|
|
30
30
|
}>;
|
|
31
|
+
export type UnmergedPathsRefusal = Readonly<{
|
|
32
|
+
kind: "unmerged-paths";
|
|
33
|
+
contractId: ContractId;
|
|
34
|
+
paths: readonly string[];
|
|
35
|
+
}>;
|
|
31
36
|
export type DeliverConflictRefusal = Readonly<{
|
|
32
37
|
kind: "integration-failed";
|
|
33
38
|
contractId: ContractId;
|
|
@@ -42,7 +47,7 @@ export type DeliverConflictRefusal = Readonly<{
|
|
|
42
47
|
export type DeliveryPreparationRefusal = Readonly<{
|
|
43
48
|
kind: "target-missing" | "worktree-missing";
|
|
44
49
|
contractId: ContractId;
|
|
45
|
-
}> | DirtyWorkspaceRefusal | IntegrationPreparationRefusal | MergeStatePresentRefusal | TargetPlacementRefusal;
|
|
50
|
+
}> | DirtyWorkspaceRefusal | UnmergedPathsRefusal | IntegrationPreparationRefusal | MergeStatePresentRefusal | TargetPlacementRefusal;
|
|
46
51
|
export type IntentRefusal = AbandonRefusal | AmendRefusal | ArcRefusal | BindRefusal | DeliverRefusal | DeliveryPreparationRefusal | DeliverConflictRefusal | PlacementRefusal | AttestationRefusal | TargetInputRefusal | VerificationDeclarationRefusal;
|
|
47
52
|
export type IntentRetry = ProtocolTerminal;
|
|
48
53
|
export type IntentOutcome<Value, Refusal = IntentRefusal> = ProtocolIntentOutcome<Value, Refusal>;
|
|
@@ -3,5 +3,3 @@ import type { GitRepository } from "../git/process.js";
|
|
|
3
3
|
import type { TaskId } from "../task/identity.js";
|
|
4
4
|
export declare function settlementFencePath(repository: GitRepository, taskId: TaskId): string;
|
|
5
5
|
export declare function acquireTaskSettlementFence(repository: GitRepository, taskId: TaskId): Promise<HeldSqliteTransactionLock>;
|
|
6
|
-
/** Remove only the hash-addressed settlement fences owned by Contract/Git reset. */
|
|
7
|
-
export declare function nukeSettlementFences(repository: GitRepository): Promise<void>;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { readdir } from "node:fs/promises";
|
|
3
2
|
import { resolve } from "node:path";
|
|
4
3
|
import { acquireSqliteTransactionLock, } from "../coordination/sqlite-transaction-lock.js";
|
|
5
4
|
import { commonGitDirectory } from "../git/repository.js";
|
|
@@ -13,26 +12,3 @@ export function acquireTaskSettlementFence(repository, taskId) {
|
|
|
13
12
|
mode: "immediate",
|
|
14
13
|
});
|
|
15
14
|
}
|
|
16
|
-
/** Remove only the hash-addressed settlement fences owned by Contract/Git reset. */
|
|
17
|
-
export async function nukeSettlementFences(repository) {
|
|
18
|
-
const root = resolve(commonGitDirectory(repository), "keiyaku", "locks", "settlement");
|
|
19
|
-
let entries;
|
|
20
|
-
try {
|
|
21
|
-
entries = await readdir(root, { withFileTypes: true });
|
|
22
|
-
}
|
|
23
|
-
catch (error) {
|
|
24
|
-
if (error.code === "ENOENT")
|
|
25
|
-
return;
|
|
26
|
-
throw error;
|
|
27
|
-
}
|
|
28
|
-
for (const entry of entries) {
|
|
29
|
-
if (!/^[0-9a-f]{64}\.sqlite$/u.test(entry.name) || !entry.isFile() || entry.isSymbolicLink()) {
|
|
30
|
-
throw new Error(`Settlement fence custody is invalid: ${resolve(root, entry.name)}`);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
for (const entry of entries) {
|
|
34
|
-
const path = resolve(root, entry.name);
|
|
35
|
-
const held = await acquireSqliteTransactionLock({ path, mode: "immediate" });
|
|
36
|
-
held.close();
|
|
37
|
-
}
|
|
38
|
-
}
|
|
@@ -12,6 +12,12 @@ export type TaskRow = Readonly<{
|
|
|
12
12
|
state: TaskState;
|
|
13
13
|
priority: TaskPriority;
|
|
14
14
|
disposition: TaskDisposition;
|
|
15
|
+
updatedAt: string;
|
|
16
|
+
bodyPresent: boolean;
|
|
17
|
+
children?: Readonly<{
|
|
18
|
+
live: number;
|
|
19
|
+
total: number;
|
|
20
|
+
}>;
|
|
15
21
|
}>;
|
|
16
22
|
export type BlockedTaskRow = TaskRow & Readonly<{
|
|
17
23
|
blockers: readonly TaskRef[];
|
|
@@ -65,14 +71,21 @@ export declare function createTaskRelations(board: TaskBoard): TaskRelationProje
|
|
|
65
71
|
export declare function taskDisposition(board: TaskBoard, task: TaskDocument): TaskDisposition;
|
|
66
72
|
export declare function taskBlocked(board: TaskBoard, task: TaskDocument): boolean;
|
|
67
73
|
export declare function projectDetailFacts(board: TaskBoard, id: TaskId, relations: TaskRelationProjection): TaskDetailFacts | null;
|
|
68
|
-
export declare function
|
|
74
|
+
export declare function projectTaskRow(board: TaskBoard, relations: TaskRelationProjection, task: TaskDocument): TaskRow;
|
|
75
|
+
export declare function projectRows(board: TaskBoard, relations: TaskRelationProjection, scope: readonly string[] | null, selection: "active" | "closed" | "all"): readonly TaskRow[];
|
|
69
76
|
export declare function projectBlocked(board: TaskBoard, scope: readonly string[] | null, relations: TaskRelationProjection): readonly BlockedTaskRow[];
|
|
70
|
-
export declare function projectStatusRows(board: TaskBoard, scope: readonly string[] | null): (Readonly<{
|
|
77
|
+
export declare function projectStatusRows(board: TaskBoard, relations: TaskRelationProjection, scope: readonly string[] | null): (Readonly<{
|
|
71
78
|
id: TaskId;
|
|
72
79
|
title: string;
|
|
73
80
|
state: TaskState;
|
|
74
81
|
priority: TaskPriority;
|
|
75
82
|
disposition: TaskDisposition;
|
|
83
|
+
updatedAt: string;
|
|
84
|
+
bodyPresent: boolean;
|
|
85
|
+
children?: Readonly<{
|
|
86
|
+
live: number;
|
|
87
|
+
total: number;
|
|
88
|
+
}>;
|
|
76
89
|
}> | {
|
|
77
90
|
blockers: readonly Readonly<{
|
|
78
91
|
id: TaskId;
|
|
@@ -84,6 +97,12 @@ export declare function projectStatusRows(board: TaskBoard, scope: readonly stri
|
|
|
84
97
|
state: TaskState;
|
|
85
98
|
priority: TaskPriority;
|
|
86
99
|
disposition: TaskDisposition;
|
|
100
|
+
updatedAt: string;
|
|
101
|
+
bodyPresent: boolean;
|
|
102
|
+
children?: Readonly<{
|
|
103
|
+
live: number;
|
|
104
|
+
total: number;
|
|
105
|
+
}>;
|
|
87
106
|
})[];
|
|
88
107
|
export type TaskBoardObservation = Readonly<{
|
|
89
108
|
statusRows: ReturnType<typeof projectStatusRows>;
|
package/build/src/task/board.js
CHANGED
|
@@ -79,33 +79,46 @@ export function projectDetailFacts(board, id, relations) {
|
|
|
79
79
|
};
|
|
80
80
|
}
|
|
81
81
|
function inScope(task, scope) { return scope === null || sameNamespace(parseTaskId(task.id).namespace, scope); }
|
|
82
|
-
export function
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
82
|
+
export function projectTaskRow(board, relations, task) {
|
|
83
|
+
const children = relations.children(task.id);
|
|
84
|
+
const total = children.length;
|
|
85
|
+
const live = children.filter((child) => child.state !== "done" && child.state !== "drop").length;
|
|
86
|
+
return {
|
|
87
|
+
id: task.id,
|
|
88
|
+
title: task.title,
|
|
89
|
+
state: task.state,
|
|
90
|
+
priority: task.priority,
|
|
86
91
|
disposition: taskDisposition(board, task),
|
|
87
|
-
|
|
92
|
+
updatedAt: task.updatedAt,
|
|
93
|
+
bodyPresent: task.body.length > 0,
|
|
94
|
+
...(total === 0 ? {} : { children: { live, total } }),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
export function projectRows(board, relations, scope, selection) {
|
|
98
|
+
return [...board.tasks.values()].filter((task) => inScope(task, scope)).filter((task) => selection === "all"
|
|
99
|
+
|| (selection === "closed") === terminal(task.state)).map((task) => projectTaskRow(board, relations, task))
|
|
100
|
+
.sort((a, b) => a.priority - b.priority || Buffer.compare(Buffer.from(a.id), Buffer.from(b.id)));
|
|
88
101
|
}
|
|
89
102
|
export function projectBlocked(board, scope, relations) {
|
|
90
|
-
return projectRows(board, scope, "active").flatMap((row) => {
|
|
103
|
+
return projectRows(board, relations, scope, "active").flatMap((row) => {
|
|
91
104
|
if (!taskBlocked(board, board.tasks.get(row.id)))
|
|
92
105
|
return [];
|
|
93
106
|
return [{ ...row, blockers: projectDetailFacts(board, row.id, relations).blockers }];
|
|
94
107
|
});
|
|
95
108
|
}
|
|
96
|
-
export function projectStatusRows(board, scope) {
|
|
97
|
-
const relations = createTaskRelations(board);
|
|
109
|
+
export function projectStatusRows(board, relations, scope) {
|
|
98
110
|
const blockers = new Map(projectBlocked(board, scope, relations).map((row) => [row.id, row.blockers]));
|
|
99
|
-
return projectRows(board, scope, "all").map((row) => {
|
|
111
|
+
return projectRows(board, relations, scope, "all").map((row) => {
|
|
100
112
|
const unresolved = blockers.get(row.id);
|
|
101
113
|
return unresolved === undefined ? row : { ...row, blockers: unresolved };
|
|
102
114
|
});
|
|
103
115
|
}
|
|
104
116
|
export function projectTaskBoardObservation(board) {
|
|
105
|
-
const
|
|
117
|
+
const relations = createTaskRelations(board);
|
|
118
|
+
const rows = projectRows(board, relations, null, "all");
|
|
106
119
|
return {
|
|
107
|
-
statusRows: projectStatusRows(board, null),
|
|
108
|
-
selectNamespace: (namespace) => projectRows(board, namespace, "all"),
|
|
120
|
+
statusRows: projectStatusRows(board, relations, null),
|
|
121
|
+
selectNamespace: (namespace) => projectRows(board, relations, namespace, "all"),
|
|
109
122
|
selectCreatedBy: (createdBy) => rows.filter((row) => board.tasks.get(row.id)?.createdBy === createdBy),
|
|
110
123
|
};
|
|
111
124
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
2
|
import { identityCoordinate, identitySegments } from "../identity/coordinates.js";
|
|
3
|
-
import {
|
|
4
|
-
const
|
|
3
|
+
import { normalizeIdentityStem } from "../identity/normalize.js";
|
|
4
|
+
const STEM_CODE_POINTS = 32;
|
|
5
5
|
export function isTaskSegment(value) {
|
|
6
6
|
return value.length > 0 && value !== "." && value !== ".." && !value.includes("/")
|
|
7
7
|
&& normalizeIdentityStem({ source: value }) === value;
|
|
@@ -30,13 +30,23 @@ export function deriveLocalStem(title) {
|
|
|
30
30
|
const stem = normalizeIdentityStem({ source: title });
|
|
31
31
|
if (stem.length === 0)
|
|
32
32
|
throw new TypeError("task title must contain a normalized identity segment");
|
|
33
|
-
|
|
33
|
+
const words = stem.split("-");
|
|
34
|
+
let fitted = words[0];
|
|
35
|
+
let count = [...fitted].length;
|
|
36
|
+
for (const word of words.slice(1)) {
|
|
37
|
+
const candidate = count + 1 + [...word].length;
|
|
38
|
+
if (candidate > STEM_CODE_POINTS)
|
|
39
|
+
break;
|
|
40
|
+
fitted += `-${word}`;
|
|
41
|
+
count = candidate;
|
|
42
|
+
}
|
|
43
|
+
return fitted;
|
|
34
44
|
}
|
|
35
45
|
export function allocateLocalId(stem, occupied) {
|
|
36
46
|
if (!occupied.has(stem))
|
|
37
47
|
return stem;
|
|
38
48
|
for (let suffix = 2; Number.isSafeInteger(suffix); suffix += 1) {
|
|
39
|
-
const candidate =
|
|
49
|
+
const candidate = `${stem}-${suffix}`;
|
|
40
50
|
if (!occupied.has(candidate))
|
|
41
51
|
return candidate;
|
|
42
52
|
}
|
|
@@ -19,6 +19,7 @@ export type TaskDoctorReport = Readonly<{
|
|
|
19
19
|
export type TaskDecompositionTree = TaskOutcome<TaskTreeNode>;
|
|
20
20
|
export type { AddTaskDocumentInput, AddTaskInput, BlockedTaskRow, TaskBatchResult, TaskCompositionResult, TaskDoctorIssue, TaskId, TaskMutationResult, TaskOutcome, TaskPriority, TaskRef, TaskRefusal, TaskRetry, TaskRow, TaskState, TaskTreeNode, TaskUpdateResult, TaskView, UpdateTaskInput, TaskPage, TaskQueryExpression, TaskQueryPredicate, TaskQueryRow, TaskQuerySort, TaskRelationPredicateField, };
|
|
21
21
|
export { TaskAuthorityCorruptionError, TASK_RELATION_PREDICATE_FIELDS };
|
|
22
|
+
export declare function observeTaskDetails(world: WorldRoot, ids: readonly TaskId[]): Promise<TaskOutcome<readonly TaskDetail[]>>;
|
|
22
23
|
declare class TaskHandle {
|
|
23
24
|
readonly id: TaskId;
|
|
24
25
|
private readonly world;
|
package/build/src/task/index.js
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { buildTree, createTaskRelations, diagnoseBoard, projectDetailFacts, } from "./board.js";
|
|
2
2
|
import { composeTasks } from "./compose.js";
|
|
3
3
|
import { TaskAuthorityCorruptionError } from "./document.js";
|
|
4
|
-
import { addTask, addTaskDocument, batchTasks, blockedTasks, lifecycleTask, listTasks, queryTasks, readyTasks, taskView, updateTask, } from "./operations.js";
|
|
4
|
+
import { addTask, addTaskDocument, batchTasks, blockedTasks, lifecycleTask, listTasks, queryTasks, readTaskDetails, readyTasks, taskView, updateTask, } from "./operations.js";
|
|
5
5
|
import { readBoard } from "./store.js";
|
|
6
6
|
import { actor, addInput, closed, limit, namespace, record, signal, sort, taskId as id, taskIds, text, updateInput, } from "./input.js";
|
|
7
7
|
import { normalizeTaskQuery, TASK_RELATION_PREDICATE_FIELDS, DEFAULT_TASK_LIMIT, } from "./query.js";
|
|
8
8
|
export { TaskAuthorityCorruptionError, TASK_RELATION_PREDICATE_FIELDS };
|
|
9
|
+
export async function observeTaskDetails(world, ids) {
|
|
10
|
+
const observed = await readTaskDetails(world, ids);
|
|
11
|
+
if (observed.kind !== "accepted")
|
|
12
|
+
return observed;
|
|
13
|
+
return { kind: "accepted", value: observed.value.map((facts) => ({ ...facts, task: taskView(facts.task) })) };
|
|
14
|
+
}
|
|
9
15
|
class TaskHandle {
|
|
10
16
|
id;
|
|
11
17
|
world;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type BlockedTaskRow, type TaskBoardObservation, type TaskRow } from "./board.js";
|
|
1
|
+
import { type BlockedTaskRow, type TaskBoardObservation, type TaskDetailFacts, type TaskRow } from "./board.js";
|
|
2
2
|
import { type TaskDocument, type TaskPriority, type TaskState } from "./document.js";
|
|
3
3
|
import { type TaskId } from "./identity.js";
|
|
4
4
|
import type { WorldRoot } from "../world.js";
|
|
@@ -115,6 +115,7 @@ export declare function lifecycleTask(world: WorldRoot, id: TaskId, verb: TaskLi
|
|
|
115
115
|
export declare function batchTasks(world: WorldRoot, verb: "done" | "drop" | "hold", ids: readonly TaskId[], signal?: AbortSignal, note?: string): Promise<TaskBatchResult>;
|
|
116
116
|
export declare function settleTask(world: WorldRoot, id: TaskId): Promise<SettledTaskResult>;
|
|
117
117
|
export declare function listTasks(world: WorldRoot, namespace: readonly string[] | undefined, selection: "active" | "closed" | "all", scope?: "namespace" | "world", limit?: number): Promise<TaskOutcome<TaskPage<TaskRow>>>;
|
|
118
|
+
export declare function readTaskDetails(world: WorldRoot, ids: readonly TaskId[]): Promise<TaskOutcome<readonly TaskDetailFacts[]>>;
|
|
118
119
|
export declare function readyTasks(world: WorldRoot, namespace: readonly string[] | undefined, scope?: "namespace" | "world", parent?: TaskId, limit?: number): Promise<TaskOutcome<TaskPage<TaskRow>>>;
|
|
119
120
|
export declare function blockedTasks(world: WorldRoot, namespace: readonly string[] | undefined, scope?: "namespace" | "world", parent?: TaskId, limit?: number): Promise<TaskOutcome<TaskPage<BlockedTaskRow>>>;
|
|
120
121
|
export declare function queryTasks(input: Readonly<{
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { documentDiff } from "../markdown/diff.js";
|
|
2
|
-
import { createTaskRelations, projectTaskBoardObservation, relationProblem, projectBlocked, projectRows, } from "./board.js";
|
|
2
|
+
import { createTaskRelations, projectDetailFacts, projectTaskBoardObservation, relationProblem, projectBlocked, projectRows, } from "./board.js";
|
|
3
3
|
import { parseTaskCreationDocument, serializeTaskDocument } from "./document.js";
|
|
4
4
|
import { allocateLocalId, deriveLocalStem, formatTaskId, parseTaskId, sameNamespace } from "./identity.js";
|
|
5
5
|
import { authorityPath, nukeTaskAuthority, readBoard, replaceAuthority, withTaskLocks, } from "./store.js";
|
|
@@ -185,7 +185,17 @@ function readScope(namespace, scope) {
|
|
|
185
185
|
}
|
|
186
186
|
export async function listTasks(world, namespace, selection, scope, limit = DEFAULT_TASK_LIMIT) {
|
|
187
187
|
const selected = readScope(namespace, scope);
|
|
188
|
-
|
|
188
|
+
const board = (await readBoard(world)).board;
|
|
189
|
+
return { kind: "accepted", value: projectPage(projectRows(board, createTaskRelations(board), selected, selection), limit) };
|
|
190
|
+
}
|
|
191
|
+
export async function readTaskDetails(world, ids) {
|
|
192
|
+
const board = (await readBoard(world)).board;
|
|
193
|
+
const relations = createTaskRelations(board);
|
|
194
|
+
const details = ids.map((id) => projectDetailFacts(board, id, relations));
|
|
195
|
+
const missing = details.findIndex((detail) => detail === null);
|
|
196
|
+
return missing < 0
|
|
197
|
+
? { kind: "accepted", value: details }
|
|
198
|
+
: { kind: "refused", refusal: { kind: "task-missing", taskId: ids[missing] } };
|
|
189
199
|
}
|
|
190
200
|
export async function readyTasks(world, namespace, scope, parent, limit = DEFAULT_TASK_LIMIT) {
|
|
191
201
|
const selected = readScope(namespace, scope);
|
|
@@ -200,7 +210,7 @@ export async function readyTasks(world, namespace, scope, parent, limit = DEFAUL
|
|
|
200
210
|
expression,
|
|
201
211
|
limit: Math.max(1, board.tasks.size),
|
|
202
212
|
}).rows;
|
|
203
|
-
const rows = selectedRows.map(({ parent: _parent, needs: _needs, blocks: _blocks, createdAt: _createdAt,
|
|
213
|
+
const rows = selectedRows.map(({ parent: _parent, needs: _needs, blocks: _blocks, createdAt: _createdAt, ...row }) => row);
|
|
204
214
|
return { kind: "accepted", value: projectPage(rows, limit) };
|
|
205
215
|
}
|
|
206
216
|
export async function blockedTasks(world, namespace, scope, parent, limit = DEFAULT_TASK_LIMIT) {
|
|
@@ -246,5 +256,6 @@ export async function observeTaskBoard(world) {
|
|
|
246
256
|
}
|
|
247
257
|
/** Internal identity catalog from one complete Task board read. */
|
|
248
258
|
export async function observeTaskCatalogRows(world) {
|
|
249
|
-
|
|
259
|
+
const board = (await readBoard(world)).board;
|
|
260
|
+
return projectRows(board, createTaskRelations(board), null, "all");
|
|
250
261
|
}
|
package/build/src/task/query.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { formatTaskId, parseTaskId, sameNamespace } from "./identity.js";
|
|
2
|
-
import { taskBlocked, taskDisposition, taskRef } from "./board.js";
|
|
2
|
+
import { projectTaskRow, taskBlocked, taskDisposition, taskRef } from "./board.js";
|
|
3
3
|
export const DEFAULT_TASK_LIMIT = 100;
|
|
4
4
|
export const MAX_TASK_LIMIT = 1_000;
|
|
5
5
|
export const TASK_RELATION_PREDICATE_FIELDS = ["under", "needs", "blocks"];
|
|
@@ -196,7 +196,7 @@ function matches(board, relations, task, expression, under) {
|
|
|
196
196
|
}
|
|
197
197
|
function row(board, relations, task) {
|
|
198
198
|
return {
|
|
199
|
-
|
|
199
|
+
...projectTaskRow(board, relations, task),
|
|
200
200
|
parent: task.parent,
|
|
201
201
|
needs: task.needs.map((need) => taskRef(board, need)),
|
|
202
202
|
blocks: relations.blocks(task.id),
|
|
@@ -5,6 +5,7 @@ export type BoardSnapshot = Readonly<{
|
|
|
5
5
|
board: TaskBoard;
|
|
6
6
|
bytes: ReadonlyMap<TaskId, Uint8Array>;
|
|
7
7
|
}>;
|
|
8
|
+
export declare const DEFAULT_TASK_LOCK_TIMEOUT_MS = 3000;
|
|
8
9
|
export declare function readBoard(world: WorldRoot): Promise<BoardSnapshot>;
|
|
9
10
|
export declare function replaceAuthority(input: Readonly<{
|
|
10
11
|
path: string;
|
|
@@ -16,6 +17,7 @@ export declare function withTaskLocks<T>(input: Readonly<{
|
|
|
16
17
|
world: WorldRoot;
|
|
17
18
|
allocation: boolean;
|
|
18
19
|
ids: readonly TaskId[];
|
|
20
|
+
timeoutMs?: number;
|
|
19
21
|
signal?: AbortSignal;
|
|
20
22
|
}>, action: () => Promise<T>): Promise<T | "busy">;
|
|
21
23
|
export declare function authorityPath(world: WorldRoot, id: TaskId): string;
|