@astrosheep/keiyaku 4.1.0 → 4.1.2
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 +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +3 -0
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +3 -0
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +3 -6
- package/build/src/akuma/akuma-errors.d.ts +6 -0
- package/build/src/akuma/akuma-errors.js +9 -0
- package/build/src/akuma/akuma-handle.d.ts +39 -0
- package/build/src/akuma/akuma-handle.js +263 -0
- package/build/src/akuma/akuma-observe.d.ts +19 -0
- package/build/src/akuma/akuma-observe.js +74 -0
- package/build/src/akuma/akuma-product-symbols.d.ts +1 -0
- package/build/src/akuma/akuma-product-symbols.js +1 -0
- package/build/src/akuma/akuma-product.d.ts +18 -0
- package/build/src/akuma/akuma-product.js +154 -0
- package/build/src/akuma/akuma.d.ts +37 -81
- package/build/src/akuma/akuma.js +39 -447
- package/build/src/akuma/allowed.d.ts +1 -0
- package/build/src/akuma/allowed.js +6 -0
- package/build/src/akuma/archetype.js +8 -5
- package/build/src/akuma/body-supervisor.d.ts +26 -0
- package/build/src/akuma/body-supervisor.js +89 -0
- package/build/src/akuma/body.d.ts +45 -4
- package/build/src/akuma/body.js +145 -35
- package/build/src/akuma/coordinate.js +17 -7
- package/build/src/akuma/heart/facts.js +1 -1
- package/build/src/akuma/heart/index.d.ts +1 -1
- package/build/src/akuma/heart/index.js +2 -4
- package/build/src/akuma/heart/request-authority.js +4 -4
- package/build/src/akuma/heart/request-rows.js +27 -16
- package/build/src/akuma/heart/rows.js +79 -41
- package/build/src/akuma/heart/soul.js +1 -1
- package/build/src/akuma/heart/storage.d.ts +1 -0
- package/build/src/akuma/heart/storage.js +76 -11
- package/build/src/akuma/heart/tells.js +35 -18
- package/build/src/akuma/heart/timeline.js +19 -11
- package/build/src/akuma/identity.d.ts +1 -0
- package/build/src/akuma/identity.js +1 -0
- package/build/src/akuma/index.d.ts +3 -3
- package/build/src/akuma/index.js +1 -1
- package/build/src/akuma/nuke.js +106 -14
- package/build/src/akuma/projection-read.d.ts +17 -0
- package/build/src/akuma/projection-read.js +135 -0
- package/build/src/akuma/projection.d.ts +1 -16
- package/build/src/akuma/projection.js +77 -154
- package/build/src/akuma/provider-recipe.js +11 -5
- package/build/src/akuma/provider.js +105 -62
- package/build/src/akuma/providers/acp/config.js +5 -6
- package/build/src/akuma/providers/acp/core.js +17 -10
- package/build/src/akuma/providers/acp/events.js +38 -29
- package/build/src/akuma/providers/acp/index.js +13 -9
- package/build/src/akuma/providers/claude/events.js +8 -12
- package/build/src/akuma/providers/claude/index.d.ts +1 -0
- package/build/src/akuma/providers/claude/index.js +55 -26
- package/build/src/akuma/providers/claude/input.js +6 -2
- package/build/src/akuma/providers/codex-app-server/events.js +61 -36
- package/build/src/akuma/providers/codex-app-server/index.js +25 -13
- package/build/src/akuma/providers/grok-build/index.js +22 -25
- package/build/src/akuma/providers/index.js +2 -18
- package/build/src/akuma/providers/opencode-sdk/events.js +32 -18
- package/build/src/akuma/providers/opencode-sdk/index.js +26 -17
- package/build/src/akuma/providers/opencode-sdk/session.js +21 -5
- package/build/src/akuma/providers/pi/events.js +26 -19
- package/build/src/akuma/providers/pi/index.js +58 -40
- package/build/src/akuma/publication.js +9 -9
- package/build/src/akuma/request-execution.d.ts +6 -0
- package/build/src/akuma/request-execution.js +86 -0
- package/build/src/akuma/request-serve.d.ts +12 -1
- package/build/src/akuma/request-serve.js +19 -122
- package/build/src/akuma/request-wire.d.ts +2 -2
- package/build/src/akuma/request-wire.js +75 -102
- package/build/src/akuma/requests.js +2 -2
- package/build/src/akuma/{body-turn.d.ts → turn-drive.d.ts} +2 -25
- package/build/src/akuma/{body-turn.js → turn-drive.js} +20 -103
- package/build/src/akuma-body.js +6 -5
- package/build/src/body/amend.d.ts +4 -1
- package/build/src/body/amend.js +14 -6
- package/build/src/body/decode.js +2 -4
- package/build/src/body/envelope.js +2 -4
- package/build/src/body/region.d.ts +1 -2
- package/build/src/body/region.js +2 -70
- package/build/src/body/render.js +17 -12
- package/build/src/body/shape.js +1 -7
- package/build/src/cli/accepted.d.ts +1 -1
- package/build/src/cli/accepted.js +8 -1
- package/build/src/cli/commands/akuma-invoke.js +18 -6
- package/build/src/cli/commands/akuma.d.ts +0 -6
- package/build/src/cli/commands/akuma.js +11 -24
- package/build/src/cli/commands/amend.d.ts +2 -2
- package/build/src/cli/commands/amend.js +2 -4
- package/build/src/cli/commands/bind.d.ts +4 -4
- package/build/src/cli/commands/bind.js +16 -6
- package/build/src/cli/commands/contract-invoke.d.ts +25 -0
- package/build/src/cli/commands/contract-invoke.js +193 -0
- package/build/src/cli/commands/contract.d.ts +109 -10
- package/build/src/cli/commands/contract.js +339 -16
- package/build/src/cli/commands/install.d.ts +1 -1
- package/build/src/cli/commands/install.js +23 -15
- package/build/src/cli/commands/task-invoke.js +187 -67
- package/build/src/cli/commands/task-query.js +48 -24
- package/build/src/cli/commands/task.js +150 -38
- package/build/src/cli/coordinates.js +2 -4
- package/build/src/cli/invoke.js +92 -208
- package/build/src/cli/main.js +4 -2
- package/build/src/cli/parse.d.ts +3 -90
- package/build/src/cli/parse.js +27 -233
- package/build/src/cli/render/akuma-activity.d.ts +38 -0
- package/build/src/cli/render/akuma-activity.js +362 -0
- package/build/src/cli/render/akuma-tool-command.js +6 -7
- package/build/src/cli/render/akuma-tool.js +10 -8
- package/build/src/cli/render/akuma.d.ts +1 -1
- package/build/src/cli/render/akuma.js +61 -353
- package/build/src/cli/render/audit.js +5 -15
- package/build/src/cli/render/board.d.ts +1 -0
- package/build/src/cli/render/board.js +17 -0
- package/build/src/cli/render/catalog.js +81 -8
- package/build/src/cli/render/contract-history.d.ts +2 -0
- package/build/src/cli/render/contract-history.js +83 -0
- package/build/src/cli/render/contract-observation.d.ts +10 -0
- package/build/src/cli/render/contract-observation.js +58 -0
- package/build/src/cli/render/contract.d.ts +1 -2
- package/build/src/cli/render/contract.js +41 -142
- package/build/src/cli/render/kanshi.js +239 -215
- package/build/src/cli/render/receipt.js +2 -9
- package/build/src/cli/render/refusal.js +10 -15
- package/build/src/cli/render/region.js +22 -6
- package/build/src/cli/render/settings.js +4 -4
- package/build/src/cli/render/task.js +34 -14
- package/build/src/cli/render/terminal.js +16 -5
- package/build/src/cli/result.d.ts +3 -3
- package/build/src/cli/runtime.d.ts +6 -1
- package/build/src/cli/runtime.js +84 -63
- package/build/src/cli/selectors.js +2 -4
- package/build/src/cli/usage.js +6 -5
- package/build/src/contract-worktree.d.ts +1 -23
- package/build/src/contract-worktree.js +79 -155
- package/build/src/coordination/durable-file.js +7 -3
- package/build/src/coordination/sqlite-transaction-lock.js +29 -12
- package/build/src/core/facts/codec.js +7 -3
- package/build/src/core/facts/types.d.ts +1 -1
- package/build/src/core/subject.js +16 -9
- package/build/src/core/verbs/abandon.d.ts +1 -1
- package/build/src/core/verbs/abandon.js +1 -1
- package/build/src/core/verbs/amend.d.ts +1 -1
- package/build/src/core/verbs/amend.js +7 -7
- package/build/src/core/verbs/attestation.js +4 -1
- package/build/src/core/verbs/bind.d.ts +1 -1
- package/build/src/core/verbs/bind.js +1 -1
- package/build/src/core/verbs/placement.d.ts +1 -1
- package/build/src/core/verbs/placement.js +10 -8
- package/build/src/dispatch/index.js +8 -6
- package/build/src/duration.js +5 -1
- package/build/src/git/admission.js +5 -3
- package/build/src/git/hooks.d.ts +8 -0
- package/build/src/git/hooks.js +63 -14
- package/build/src/git/identity.js +1 -4
- package/build/src/git/integration.d.ts +2 -3
- package/build/src/git/integration.js +32 -39
- package/build/src/git/nuke.d.ts +1 -1
- package/build/src/git/nuke.js +110 -57
- package/build/src/git/observe.d.ts +13 -1
- package/build/src/git/observe.js +51 -44
- package/build/src/git/process.js +4 -2
- package/build/src/git/read-observation.js +10 -8
- package/build/src/git/reconcile.d.ts +22 -2
- package/build/src/git/reconcile.js +38 -274
- package/build/src/git/repository.d.ts +2 -0
- package/build/src/git/repository.js +13 -22
- package/build/src/git/scratch.d.ts +8 -5
- package/build/src/git/scratch.js +29 -2
- package/build/src/git/target-placement.d.ts +9 -10
- package/build/src/git/target-placement.js +59 -62
- package/build/src/git/tender.js +13 -46
- package/build/src/git/terminal-reconcile.d.ts +21 -0
- package/build/src/git/terminal-reconcile.js +278 -0
- package/build/src/git/terminal-seal.js +15 -12
- package/build/src/git/tree.js +5 -2
- package/build/src/git/workspace.d.ts +24 -2
- package/build/src/git/workspace.js +120 -16
- package/build/src/identity/coordinates.js +3 -1
- package/build/src/identity/selector.js +6 -3
- package/build/src/kanshi/fleet.d.ts +6 -0
- package/build/src/kanshi/fleet.js +27 -0
- package/build/src/kanshi/index.d.ts +1 -1
- package/build/src/kanshi/read.js +60 -48
- package/build/src/kanshi/report.d.ts +11 -23
- package/build/src/kanshi/select.js +25 -24
- package/build/src/library/address.d.ts +2 -1
- package/build/src/library/address.js +7 -8
- package/build/src/library/akuma-creation.js +20 -12
- package/build/src/library/audit.d.ts +0 -1
- package/build/src/library/audit.js +1 -2
- package/build/src/library/bind.d.ts +53 -5
- package/build/src/library/bind.js +98 -22
- package/build/src/library/catalog.d.ts +1 -0
- package/build/src/library/catalog.js +6 -3
- package/build/src/library/configuration.js +3 -1
- package/build/src/library/continuation.d.ts +15 -4
- package/build/src/library/continuation.js +24 -16
- package/build/src/library/contract-bind.d.ts +26 -0
- package/build/src/library/contract-bind.js +106 -0
- package/build/src/library/contract-operations.d.ts +77 -0
- package/build/src/library/contract-operations.js +130 -0
- package/build/src/library/contract-types.d.ts +95 -0
- package/build/src/library/contract-types.js +1 -0
- package/build/src/library/contract.d.ts +19 -154
- package/build/src/library/contract.js +46 -261
- package/build/src/library/fleet.d.ts +1 -1
- package/build/src/library/fleet.js +19 -11
- package/build/src/library/input.d.ts +2 -0
- package/build/src/library/input.js +14 -0
- package/build/src/library/keiyaku.d.ts +8 -7
- package/build/src/library/keiyaku.js +3 -3
- package/build/src/library/nuke.js +14 -5
- package/build/src/library/reconcile.d.ts +9 -4
- package/build/src/library/reconcile.js +30 -24
- package/build/src/library/refusal.d.ts +13 -5
- package/build/src/library/refusal.js +7 -0
- package/build/src/library/region.d.ts +8 -7
- package/build/src/library/region.js +31 -11
- package/build/src/library/repo.js +2 -2
- package/build/src/markdown/lex.js +9 -2
- package/build/src/markdown/parse.js +11 -7
- package/build/src/markdown/query.js +4 -2
- package/build/src/protocol/amend.js +3 -1
- package/build/src/protocol/attempt.d.ts +7 -3
- package/build/src/protocol/attempt.js +9 -5
- package/build/src/protocol/audit.d.ts +1 -1
- package/build/src/protocol/audit.js +4 -14
- package/build/src/protocol/bind.d.ts +19 -6
- package/build/src/protocol/bind.js +28 -13
- package/build/src/protocol/completion.d.ts +0 -1
- package/build/src/protocol/completion.js +1 -3
- package/build/src/protocol/deliver.d.ts +1 -3
- package/build/src/protocol/deliver.js +15 -39
- package/build/src/protocol/intent.d.ts +1 -0
- package/build/src/protocol/intent.js +9 -7
- package/build/src/protocol/operations.d.ts +3 -7
- package/build/src/protocol/operations.js +5 -3
- package/build/src/protocol/placement.d.ts +0 -1
- package/build/src/protocol/placement.js +6 -6
- package/build/src/protocol/read/observation.d.ts +11 -0
- package/build/src/protocol/read/observation.js +37 -0
- package/build/src/protocol/read/status.d.ts +23 -12
- package/build/src/protocol/read/status.js +90 -72
- package/build/src/protocol/reconcile.d.ts +1 -0
- package/build/src/protocol/reconcile.js +2 -3
- package/build/src/protocol/reintegrate.js +1 -1
- package/build/src/protocol/review.d.ts +0 -1
- package/build/src/protocol/review.js +15 -22
- package/build/src/protocol/run.d.ts +1 -0
- package/build/src/protocol/run.js +4 -5
- package/build/src/runtime/proc/launch.d.ts +33 -0
- package/build/src/runtime/proc/launch.js +98 -0
- package/build/src/runtime/proc/line-rpc.js +9 -3
- package/build/src/runtime/proc/run.d.ts +12 -6
- package/build/src/runtime/proc/run.js +78 -21
- package/build/src/runtime/proc/stdio.js +7 -10
- package/build/src/settings.js +6 -2
- package/build/src/settlement/holder.js +13 -10
- package/build/src/settlement/settle.js +39 -6
- package/build/src/task/board.js +12 -6
- package/build/src/task/compose.js +85 -22
- package/build/src/task/context.js +21 -8
- package/build/src/task/document.js +46 -10
- package/build/src/task/identity.js +5 -2
- package/build/src/task/index.d.ts +0 -1
- package/build/src/task/index.js +119 -34
- package/build/src/task/input.js +29 -7
- package/build/src/task/mutation.js +61 -19
- package/build/src/task/operations.d.ts +6 -1
- package/build/src/task/operations.js +83 -29
- package/build/src/task/query.js +55 -26
- package/build/src/task/store.d.ts +3 -1
- package/build/src/task/store.js +81 -17
- package/build/src/verification/execution.js +14 -11
- package/build/src/workspace-place.js +19 -7
- package/build/src/world.js +16 -6
- package/package.json +8 -3
|
@@ -1,34 +1,58 @@
|
|
|
1
1
|
import { chmod, lstat, readFile, unlink } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { acquireSqliteTransactionLock } from "./coordination/sqlite-transaction-lock.js";
|
|
4
|
-
import {
|
|
4
|
+
import { repairDerivedFile } from "./coordination/durable-file.js";
|
|
5
5
|
import { contractId } from "./core/facts/types.js";
|
|
6
|
-
import {
|
|
6
|
+
import { worktreeGitDirectory, worktreeRoot } from "./git/repository.js";
|
|
7
7
|
import { GitPlumbingError, runGit } from "./git/process.js";
|
|
8
8
|
import { worktreePath } from "./git/workspace.js";
|
|
9
|
-
import { appointmentFor, placeRegisterPath
|
|
9
|
+
import { appointmentFor, placeRegisterPath } from "./workspace-place.js";
|
|
10
10
|
const IGNORE_BYTES = ".gitignore\nKEIYAKU.md\n";
|
|
11
|
+
const PRIMARY_IGNORE_BYTES = "*\n!settings.json\n!tasks/\n!tasks/**\n";
|
|
11
12
|
const APPOINTMENT_DESCRIPTION = "This is a read-only projection. Do not edit manually.";
|
|
12
|
-
function appointmentBytes(contract) {
|
|
13
|
+
function appointmentBytes(contract) {
|
|
14
|
+
return `---\ncontract: ${contract}\ndescription: ${APPOINTMENT_DESCRIPTION}\n---\n`;
|
|
15
|
+
}
|
|
13
16
|
function renderArc(arc) {
|
|
14
17
|
return [
|
|
15
|
-
"## Arc",
|
|
16
|
-
"
|
|
18
|
+
"## Arc",
|
|
19
|
+
"",
|
|
20
|
+
"### Sequence",
|
|
21
|
+
"",
|
|
22
|
+
String(arc.seq),
|
|
23
|
+
"",
|
|
24
|
+
"### Title",
|
|
25
|
+
"",
|
|
26
|
+
arc.title.trimEnd(),
|
|
27
|
+
"",
|
|
28
|
+
"### Objective",
|
|
29
|
+
"",
|
|
30
|
+
arc.objective.trimEnd(),
|
|
31
|
+
"",
|
|
32
|
+
"### Brief",
|
|
33
|
+
"",
|
|
34
|
+
arc.brief.trimEnd(),
|
|
17
35
|
].join("\n");
|
|
18
36
|
}
|
|
19
37
|
const FULFILLMENT = [
|
|
20
|
-
"## Fulfillment",
|
|
38
|
+
"## Fulfillment",
|
|
39
|
+
"",
|
|
40
|
+
"### Appointment",
|
|
41
|
+
"",
|
|
21
42
|
"Each commission names exactly one seat: Deliverer or Reviewer.",
|
|
22
|
-
"If no seat was named, stop and ask the caller. Never infer it.",
|
|
23
|
-
"
|
|
43
|
+
"If no seat was named, stop and ask the caller. Never infer it.",
|
|
44
|
+
"",
|
|
45
|
+
"### Worktree",
|
|
46
|
+
"",
|
|
24
47
|
"This file is a derived view of the journal-authoritative Contract. Never edit it to change the Contract.",
|
|
25
48
|
"Treat the directory containing `.keiyaku/KEIYAKU.md` as the Contract worktree root.",
|
|
26
|
-
"Read the complete Contract before acting and keep work inside that worktree.",
|
|
27
|
-
"
|
|
49
|
+
"Read the complete Contract before acting and keep work inside that worktree.",
|
|
50
|
+
"",
|
|
51
|
+
"### Deliverer",
|
|
52
|
+
"",
|
|
28
53
|
"Implement and verify the Objective under the Design, Region, and Criteria in this Contract.",
|
|
29
54
|
"When an Arc is active, stay within that current chapter.",
|
|
30
|
-
"For work requiring three or more steps, prefer `keiyaku task -C <worktree>` "
|
|
31
|
-
+ "to organize and manage Tasks.",
|
|
55
|
+
"For work requiring three or more steps, prefer `keiyaku task -C <worktree>` " + "to organize and manage Tasks.",
|
|
32
56
|
"Promptly update progress for Tasks already present in the current worktree.",
|
|
33
57
|
"Leave lifecycle decisions to the caller; report the candidate, verification performed, and any unmet term.",
|
|
34
58
|
"Deliver from this worktree. A clean worktree delivers HEAD; uncommitted work",
|
|
@@ -38,7 +62,8 @@ const FULFILLMENT = [
|
|
|
38
62
|
"and deliver again; while the merge stays uncommitted that continuation uses",
|
|
39
63
|
"`--include-dirty`. Unresolved paths always refuse.",
|
|
40
64
|
"",
|
|
41
|
-
"### Reviewer",
|
|
65
|
+
"### Reviewer",
|
|
66
|
+
"",
|
|
42
67
|
"Review this Contract worktree against the complete Contract.",
|
|
43
68
|
"Review the complete current worktree snapshot, not a named candidate commit.",
|
|
44
69
|
"Do not modify it; report covered Criteria, findings, and missing evidence.",
|
|
@@ -49,7 +74,9 @@ export function renderContractGuidance(state) {
|
|
|
49
74
|
state.terms.document.bytes.trimEnd(),
|
|
50
75
|
...(state.currentArc === undefined ? [] : [renderArc(state.currentArc.data)]),
|
|
51
76
|
FULFILLMENT,
|
|
52
|
-
]
|
|
77
|
+
]
|
|
78
|
+
.join("\n\n")
|
|
79
|
+
.concat("\n");
|
|
53
80
|
}
|
|
54
81
|
async function isTracked(repository, relativePath) {
|
|
55
82
|
try {
|
|
@@ -88,57 +115,6 @@ export async function readContractAppointment(repository) {
|
|
|
88
115
|
const contract = appointedContract(await readFile(path, "utf8"));
|
|
89
116
|
return contract === undefined ? { kind: "invalid", path } : { kind: "appointed", path, contract };
|
|
90
117
|
}
|
|
91
|
-
async function readHereContractWorkspaceAppointment(repository, contract) {
|
|
92
|
-
const matches = [];
|
|
93
|
-
let worktrees;
|
|
94
|
-
try {
|
|
95
|
-
worktrees = await registeredWorktrees(repository);
|
|
96
|
-
}
|
|
97
|
-
catch (error) {
|
|
98
|
-
if (error instanceof GitPlumbingError)
|
|
99
|
-
return { kind: "failed", diagnostic: error.message };
|
|
100
|
-
throw error;
|
|
101
|
-
}
|
|
102
|
-
for (const worktree of worktrees) {
|
|
103
|
-
const appointment = await readContractAppointment({ ...repository, effectiveCwd: worktree.path });
|
|
104
|
-
if (appointment.kind === "appointed" && appointment.contract === contract)
|
|
105
|
-
matches.push(worktree.path);
|
|
106
|
-
}
|
|
107
|
-
if (matches.length === 0)
|
|
108
|
-
return { kind: "unappointed" };
|
|
109
|
-
if (matches.length > 1)
|
|
110
|
-
return { kind: "conflicted", paths: matches.sort() };
|
|
111
|
-
return { kind: "appointed", path: matches[0] };
|
|
112
|
-
}
|
|
113
|
-
function conflictDiagnostic(paths) {
|
|
114
|
-
const displayed = paths.slice(0, 3);
|
|
115
|
-
return `duplicate here Contract workspace appointments: ${displayed.join(", ")}${paths.length > displayed.length ? ", ..." : ""}`;
|
|
116
|
-
}
|
|
117
|
-
/** Fold existing here appointments over all registered worktrees for one Contract. */
|
|
118
|
-
export async function resolveHereContractWorkspace(repository, contract) {
|
|
119
|
-
const appointment = await readHereContractWorkspaceAppointment(repository, contract);
|
|
120
|
-
if (appointment.kind === "failed")
|
|
121
|
-
return { kind: "failed", diagnostic: appointment.diagnostic, cause: "observation" };
|
|
122
|
-
if (appointment.kind === "conflicted") {
|
|
123
|
-
return { kind: "failed", diagnostic: conflictDiagnostic(appointment.paths), cause: "duplicate" };
|
|
124
|
-
}
|
|
125
|
-
return appointment;
|
|
126
|
-
}
|
|
127
|
-
export async function reserveContractWorktree(repository, contract) {
|
|
128
|
-
const worktree = await worktreeRoot(repository);
|
|
129
|
-
const scoped = { ...repository, effectiveCwd: worktree };
|
|
130
|
-
const ignore = generatedPath(worktree, ".gitignore");
|
|
131
|
-
if (await isTracked(scoped, ".keiyaku/.gitignore"))
|
|
132
|
-
throw new Error(`generated path is tracked by Git: ${ignore}`);
|
|
133
|
-
await repairDerivedFile(ignore, IGNORE_BYTES);
|
|
134
|
-
const path = generatedPath(worktree, "KEIYAKU.md");
|
|
135
|
-
if (await isTracked(scoped, ".keiyaku/KEIYAKU.md"))
|
|
136
|
-
throw new Error(`generated path is tracked by Git: ${path}`);
|
|
137
|
-
if (await createFileDurablyExclusive(path, appointmentBytes(contract), 0o444))
|
|
138
|
-
return { kind: "reserved", path };
|
|
139
|
-
const appointment = await readContractAppointment(repository);
|
|
140
|
-
return appointment.kind === "absent" ? { kind: "invalid", path } : appointment;
|
|
141
|
-
}
|
|
142
118
|
export async function withContractWorktreeAppointment(repository, action) {
|
|
143
119
|
const root = await worktreeRoot(repository);
|
|
144
120
|
const lock = await acquireSqliteTransactionLock({
|
|
@@ -156,39 +132,13 @@ export async function releaseContractWorktree(repository, contract) {
|
|
|
156
132
|
const appointment = await readContractAppointment(repository);
|
|
157
133
|
if (appointment.kind !== "appointed" || appointment.contract !== contract)
|
|
158
134
|
return;
|
|
159
|
-
if (await readFile(appointment.path, "utf8") !== appointmentBytes(contract))
|
|
135
|
+
if ((await readFile(appointment.path, "utf8")) !== appointmentBytes(contract))
|
|
160
136
|
return;
|
|
161
137
|
try {
|
|
162
138
|
await unlink(appointment.path);
|
|
163
139
|
}
|
|
164
|
-
catch {
|
|
165
|
-
|
|
166
|
-
async function removeOwnedHereSupport(worktree) {
|
|
167
|
-
const path = generatedPath(worktree, ".gitignore");
|
|
168
|
-
try {
|
|
169
|
-
const value = await lstat(path);
|
|
170
|
-
if (!value.isFile() || value.isSymbolicLink())
|
|
171
|
-
return 0;
|
|
172
|
-
if (await readFile(path, "utf8") !== IGNORE_BYTES)
|
|
173
|
-
return 0;
|
|
174
|
-
await unlink(path);
|
|
175
|
-
return 1;
|
|
176
|
-
}
|
|
177
|
-
catch (error) {
|
|
178
|
-
if (error.code === "ENOENT")
|
|
179
|
-
return 0;
|
|
180
|
-
throw error;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
/** Remove matching here appointments and their exact generated support from registered worktrees. */
|
|
184
|
-
export async function nukeHereAppointments(repository) {
|
|
185
|
-
for (const registered of await registeredWorktrees(repository)) {
|
|
186
|
-
const scoped = { ...repository, effectiveCwd: registered.path };
|
|
187
|
-
const appointment = await readContractAppointment(scoped);
|
|
188
|
-
if (appointment.kind === "appointed") {
|
|
189
|
-
await unlink(appointment.path);
|
|
190
|
-
await removeOwnedHereSupport(registered.path);
|
|
191
|
-
}
|
|
140
|
+
catch {
|
|
141
|
+
/* reservation cleanup is best effort */
|
|
192
142
|
}
|
|
193
143
|
}
|
|
194
144
|
async function repair(repository, worktree, relativePath, bytes) {
|
|
@@ -200,11 +150,31 @@ async function repair(repository, worktree, relativePath, bytes) {
|
|
|
200
150
|
try {
|
|
201
151
|
await chmod(path, 0o444);
|
|
202
152
|
}
|
|
203
|
-
catch {
|
|
153
|
+
catch {
|
|
154
|
+
/* advisory protection only */
|
|
155
|
+
}
|
|
204
156
|
}
|
|
205
157
|
return { kind: "contract-file", path, action };
|
|
206
158
|
}
|
|
207
159
|
async function materialize(repository, worktree, guidance) {
|
|
160
|
+
const effects = [];
|
|
161
|
+
const failed = (target, path, error) => ({
|
|
162
|
+
effects,
|
|
163
|
+
lag: [
|
|
164
|
+
{
|
|
165
|
+
kind: "contract-file-failed",
|
|
166
|
+
worktree: target,
|
|
167
|
+
path,
|
|
168
|
+
diagnostic: error instanceof Error ? error.message : String(error),
|
|
169
|
+
},
|
|
170
|
+
],
|
|
171
|
+
});
|
|
172
|
+
try {
|
|
173
|
+
effects.push(await repair({ ...repository, effectiveCwd: repository.primaryWorktree }, repository.primaryWorktree, ".keiyaku/.gitignore", PRIMARY_IGNORE_BYTES));
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
return failed(repository.primaryWorktree, join(repository.primaryWorktree, ".keiyaku", ".gitignore"), error);
|
|
177
|
+
}
|
|
208
178
|
const stat = await lstat(worktree).catch((error) => {
|
|
209
179
|
if (error.code === "ENOENT")
|
|
210
180
|
return undefined;
|
|
@@ -212,17 +182,18 @@ async function materialize(repository, worktree, guidance) {
|
|
|
212
182
|
});
|
|
213
183
|
if (stat === undefined || !stat.isDirectory()) {
|
|
214
184
|
return {
|
|
215
|
-
effects
|
|
216
|
-
lag: [
|
|
185
|
+
effects,
|
|
186
|
+
lag: [
|
|
187
|
+
{
|
|
217
188
|
kind: "contract-file-failed",
|
|
218
189
|
worktree,
|
|
219
190
|
path: generatedPath(worktree, "KEIYAKU.md"),
|
|
220
191
|
diagnostic: "Contract worktree is unavailable",
|
|
221
|
-
}
|
|
192
|
+
},
|
|
193
|
+
],
|
|
222
194
|
};
|
|
223
195
|
}
|
|
224
196
|
const scoped = { ...repository, effectiveCwd: worktree };
|
|
225
|
-
const effects = [];
|
|
226
197
|
for (const [relativePath, bytes] of [
|
|
227
198
|
[".keiyaku/.gitignore", IGNORE_BYTES],
|
|
228
199
|
[".keiyaku/KEIYAKU.md", guidance],
|
|
@@ -231,73 +202,26 @@ async function materialize(repository, worktree, guidance) {
|
|
|
231
202
|
effects.push(await repair(scoped, worktree, relativePath, bytes));
|
|
232
203
|
}
|
|
233
204
|
catch (error) {
|
|
234
|
-
return
|
|
235
|
-
effects,
|
|
236
|
-
lag: [{
|
|
237
|
-
kind: "contract-file-failed",
|
|
238
|
-
worktree,
|
|
239
|
-
path: join(worktree, relativePath),
|
|
240
|
-
diagnostic: error instanceof Error ? error.message : String(error),
|
|
241
|
-
}],
|
|
242
|
-
};
|
|
205
|
+
return failed(worktree, join(worktree, relativePath), error);
|
|
243
206
|
}
|
|
244
207
|
}
|
|
245
208
|
return { effects, lag: [] };
|
|
246
209
|
}
|
|
247
|
-
async function
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
return await materialize(repository, appointment.path, guidance);
|
|
251
|
-
if (appointment.kind === "failed") {
|
|
252
|
-
if (onConflict !== undefined)
|
|
253
|
-
return onConflict(appointment.diagnostic);
|
|
254
|
-
throw new Error(appointment.diagnostic);
|
|
255
|
-
}
|
|
256
|
-
return {
|
|
257
|
-
effects: [],
|
|
258
|
-
lag: [{
|
|
259
|
-
kind: "contract-file-failed",
|
|
260
|
-
worktree: repository.primaryWorktree,
|
|
261
|
-
path: generatedPath(repository.primaryWorktree, "KEIYAKU.md"),
|
|
262
|
-
diagnostic: "here Contract is unappointed",
|
|
263
|
-
}],
|
|
264
|
-
};
|
|
265
|
-
}
|
|
266
|
-
async function removeHere(repository, state) {
|
|
267
|
-
const effects = [];
|
|
268
|
-
for (const worktree of await registeredWorktrees(repository)) {
|
|
269
|
-
const appointment = await readContractAppointment({ ...repository, effectiveCwd: worktree.path });
|
|
270
|
-
if (appointment.kind !== "appointed" || appointment.contract !== state.id)
|
|
271
|
-
continue;
|
|
272
|
-
try {
|
|
273
|
-
await unlink(appointment.path);
|
|
274
|
-
effects.push({ kind: "contract-file", path: appointment.path, action: "removed" });
|
|
275
|
-
}
|
|
276
|
-
catch (error) {
|
|
277
|
-
return { effects, lag: [{ kind: "contract-file-failed", worktree: worktree.path, path: appointment.path, diagnostic: error instanceof Error ? error.message : String(error) }] };
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
return { effects, lag: [] };
|
|
281
|
-
}
|
|
282
|
-
export async function projectContractWorktree(repository, state, register, onConflict) {
|
|
283
|
-
if (state === null || state.terminal) {
|
|
284
|
-
return state?.coordinates.workspace === "here"
|
|
285
|
-
? await removeHere(repository, state)
|
|
286
|
-
: { effects: [], lag: [] };
|
|
287
|
-
}
|
|
288
|
-
if (state.coordinates.workspace === "here") {
|
|
289
|
-
return await here(repository, state, renderContractGuidance(state), onConflict);
|
|
290
|
-
}
|
|
210
|
+
export async function projectContractWorktree(repository, state, register, _onConflict) {
|
|
211
|
+
if (state === null || state.terminal)
|
|
212
|
+
return { effects: [], lag: [] };
|
|
291
213
|
const appointed = register === undefined ? undefined : appointmentFor(register, state.id);
|
|
292
214
|
if (appointed === undefined) {
|
|
293
215
|
return {
|
|
294
216
|
effects: [],
|
|
295
|
-
lag: [
|
|
217
|
+
lag: [
|
|
218
|
+
{
|
|
296
219
|
kind: "contract-file-failed",
|
|
297
220
|
worktree: repository.primaryWorktree,
|
|
298
221
|
path: placeRegisterPath(repository),
|
|
299
222
|
diagnostic: "managed Contract is unappointed",
|
|
300
|
-
}
|
|
223
|
+
},
|
|
224
|
+
],
|
|
301
225
|
};
|
|
302
226
|
}
|
|
303
227
|
return await materialize(repository, worktreePath(repository, appointed.place), renderContractGuidance(state));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
|
-
import { closeSync, fsyncSync, openSync, renameSync, writeFileSync
|
|
2
|
+
import { closeSync, fsyncSync, openSync, renameSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { lstat, mkdir, readFile, unlink } from "node:fs/promises";
|
|
4
4
|
import { dirname, resolve } from "node:path";
|
|
5
5
|
export function syncDirectory(path) {
|
|
@@ -34,7 +34,9 @@ export async function replaceFileDurably(path, bytes) {
|
|
|
34
34
|
try {
|
|
35
35
|
await unlink(temporary);
|
|
36
36
|
}
|
|
37
|
-
catch {
|
|
37
|
+
catch {
|
|
38
|
+
/* renamed, absent, or best-effort cleanup */
|
|
39
|
+
}
|
|
38
40
|
if (error.code !== "EEXIST")
|
|
39
41
|
throw error;
|
|
40
42
|
}
|
|
@@ -61,7 +63,9 @@ export async function createFileDurablyExclusive(path, bytes, mode = 0o600) {
|
|
|
61
63
|
try {
|
|
62
64
|
await unlink(path);
|
|
63
65
|
}
|
|
64
|
-
catch {
|
|
66
|
+
catch {
|
|
67
|
+
/* absent or best-effort cleanup */
|
|
68
|
+
}
|
|
65
69
|
throw error;
|
|
66
70
|
}
|
|
67
71
|
}
|
|
@@ -21,9 +21,11 @@ function isBusy(error) {
|
|
|
21
21
|
return false;
|
|
22
22
|
const value = error;
|
|
23
23
|
const message = String(value.message ?? "").toLowerCase();
|
|
24
|
-
return value.errcode === SQLITE_BUSY ||
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
return (value.errcode === SQLITE_BUSY ||
|
|
25
|
+
value.code === "ERR_SQLITE_BUSY" ||
|
|
26
|
+
value.code === "ERR_SQLITE_LOCKED" ||
|
|
27
|
+
message.includes("database is locked") ||
|
|
28
|
+
message.includes("database is busy"));
|
|
27
29
|
}
|
|
28
30
|
function closeDatabase(database) {
|
|
29
31
|
let failure;
|
|
@@ -56,7 +58,9 @@ async function openLock(path, mode) {
|
|
|
56
58
|
catch (error) {
|
|
57
59
|
if (error instanceof SqliteTransactionLockError)
|
|
58
60
|
throw error;
|
|
59
|
-
throw new SqliteTransactionLockError(`cannot prepare SQLite lock: ${detail(error)}`, "open-failed", {
|
|
61
|
+
throw new SqliteTransactionLockError(`cannot prepare SQLite lock: ${detail(error)}`, "open-failed", {
|
|
62
|
+
cause: error,
|
|
63
|
+
});
|
|
60
64
|
}
|
|
61
65
|
let database;
|
|
62
66
|
try {
|
|
@@ -72,32 +76,45 @@ async function openLock(path, mode) {
|
|
|
72
76
|
closeDatabase(database);
|
|
73
77
|
if (isBusy(error))
|
|
74
78
|
throw error;
|
|
75
|
-
throw new SqliteTransactionLockError(`cannot acquire SQLite lock: ${detail(error)}`, "open-failed", {
|
|
79
|
+
throw new SqliteTransactionLockError(`cannot acquire SQLite lock: ${detail(error)}`, "open-failed", {
|
|
80
|
+
cause: error,
|
|
81
|
+
});
|
|
76
82
|
}
|
|
77
83
|
let closed = false;
|
|
78
|
-
return {
|
|
84
|
+
return {
|
|
85
|
+
close() {
|
|
79
86
|
if (closed)
|
|
80
87
|
return;
|
|
81
88
|
closed = true;
|
|
82
89
|
const failure = closeDatabase(database);
|
|
83
90
|
if (failure !== undefined) {
|
|
84
|
-
throw new SqliteTransactionLockError(`cannot release SQLite lock: ${detail(failure)}`, "release-failed", {
|
|
91
|
+
throw new SqliteTransactionLockError(`cannot release SQLite lock: ${detail(failure)}`, "release-failed", {
|
|
92
|
+
cause: failure,
|
|
93
|
+
});
|
|
85
94
|
}
|
|
86
|
-
}
|
|
95
|
+
},
|
|
96
|
+
};
|
|
87
97
|
}
|
|
88
98
|
function wait(milliseconds, signal) {
|
|
89
99
|
return new Promise((resolve, reject) => {
|
|
90
|
-
const finish = () => {
|
|
100
|
+
const finish = () => {
|
|
101
|
+
signal?.removeEventListener("abort", abort);
|
|
102
|
+
resolve();
|
|
103
|
+
};
|
|
91
104
|
const timer = setTimeout(finish, milliseconds);
|
|
92
|
-
const abort = () => {
|
|
105
|
+
const abort = () => {
|
|
106
|
+
clearTimeout(timer);
|
|
107
|
+
signal?.removeEventListener("abort", abort);
|
|
108
|
+
reject(signal?.reason);
|
|
109
|
+
};
|
|
93
110
|
signal?.addEventListener("abort", abort, { once: true });
|
|
94
111
|
if (signal?.aborted)
|
|
95
112
|
abort();
|
|
96
113
|
});
|
|
97
114
|
}
|
|
98
115
|
export async function acquireSqliteTransactionLock(input) {
|
|
99
|
-
if (input.path.length === 0 ||
|
|
100
|
-
&& (!Number.isSafeInteger(input.timeoutMs) || input.timeoutMs < 0))) {
|
|
116
|
+
if (input.path.length === 0 ||
|
|
117
|
+
(input.timeoutMs !== undefined && (!Number.isSafeInteger(input.timeoutMs) || input.timeoutMs < 0))) {
|
|
101
118
|
throw new SqliteTransactionLockError("SQLite lock path and timeout must be valid", "invalid");
|
|
102
119
|
}
|
|
103
120
|
const started = performance.now();
|
|
@@ -87,7 +87,8 @@ function validateTimestamp(value, path) {
|
|
|
87
87
|
}
|
|
88
88
|
const milliseconds = Date.parse(text);
|
|
89
89
|
const canonical = Number.isFinite(milliseconds) ? new Date(milliseconds).toISOString() : "";
|
|
90
|
-
if (!Number.isFinite(milliseconds) ||
|
|
90
|
+
if (!Number.isFinite(milliseconds) ||
|
|
91
|
+
(text.includes(".") ? canonical !== text : canonical.replace(".000Z", "Z") !== text)) {
|
|
91
92
|
fail(path, "expected a real canonical UTC timestamp");
|
|
92
93
|
}
|
|
93
94
|
return text;
|
|
@@ -96,7 +97,7 @@ function validateCoordinates(value, path) {
|
|
|
96
97
|
const object = requireRecord(value, path);
|
|
97
98
|
requireKeys(object, ["start", "workspace"], path, ["target"]);
|
|
98
99
|
const workspace = stringValue(object.workspace, `${path}.workspace`);
|
|
99
|
-
if (workspace !== "worktree"
|
|
100
|
+
if (workspace !== "worktree")
|
|
100
101
|
fail(`${path}.workspace`, "unknown workspace");
|
|
101
102
|
return {
|
|
102
103
|
start: brandedValue(object.start, `${path}.start`, snapshotId),
|
|
@@ -282,5 +283,8 @@ export function decodeJournal(journal) {
|
|
|
282
283
|
return [];
|
|
283
284
|
if (journal.includes("\r") || !journal.endsWith("\n"))
|
|
284
285
|
throw new AuthorityCorruptionError("journal must use LF lines and end with LF");
|
|
285
|
-
return journal
|
|
286
|
+
return journal
|
|
287
|
+
.slice(0, -1)
|
|
288
|
+
.split("\n")
|
|
289
|
+
.map((line) => decodeEntry(line));
|
|
286
290
|
}
|
|
@@ -56,7 +56,7 @@ export declare function gate(value: string): Gate;
|
|
|
56
56
|
export type ContractCoordinates = Readonly<{
|
|
57
57
|
start: SnapshotId;
|
|
58
58
|
target?: string;
|
|
59
|
-
workspace: "worktree"
|
|
59
|
+
workspace: "worktree";
|
|
60
60
|
}>;
|
|
61
61
|
export type ContractTerms = Readonly<{
|
|
62
62
|
document: Readonly<{
|
|
@@ -12,11 +12,16 @@ function parseKey(value, index) {
|
|
|
12
12
|
}
|
|
13
13
|
try {
|
|
14
14
|
switch (value[0]) {
|
|
15
|
-
case "document":
|
|
16
|
-
|
|
17
|
-
case "
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
case "document":
|
|
16
|
+
return { kind: "document", value: documentKey(value[1]) };
|
|
17
|
+
case "segment":
|
|
18
|
+
return { kind: "segment", value: documentSegmentKey(value[1]) };
|
|
19
|
+
case "snapshot":
|
|
20
|
+
return { kind: "snapshot", value: snapshotId(value[1]) };
|
|
21
|
+
case "change":
|
|
22
|
+
return { kind: "change", value: changeId(value[1]) };
|
|
23
|
+
default:
|
|
24
|
+
throw new AuthorityCorruptionError(`unknown dependency key kind '${value[0]}'`);
|
|
20
25
|
}
|
|
21
26
|
}
|
|
22
27
|
catch (error) {
|
|
@@ -69,10 +74,12 @@ function currentKeys(state) {
|
|
|
69
74
|
return new Set([
|
|
70
75
|
encodeKey({ kind: "document", value: state.terms.document.key }),
|
|
71
76
|
...state.terms.segments.map((value) => encodeKey({ kind: "segment", value })),
|
|
72
|
-
...(integration === undefined
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
77
|
+
...(integration === undefined
|
|
78
|
+
? []
|
|
79
|
+
: [
|
|
80
|
+
encodeKey({ kind: "snapshot", value: integration.snapshot }),
|
|
81
|
+
encodeKey({ kind: "change", value: integration.changeId }),
|
|
82
|
+
]),
|
|
76
83
|
]);
|
|
77
84
|
}
|
|
78
85
|
/** Build one memoized currentness test for a folded contract snapshot. */
|
|
@@ -10,4 +10,4 @@ export type AbandonRefusal = Readonly<{
|
|
|
10
10
|
kind: "contract-missing" | "terminal";
|
|
11
11
|
contractId: ContractId;
|
|
12
12
|
}>;
|
|
13
|
-
export declare function decideAbandon({ input, attempt, observation }: DecideInput<AbandonInput>): OfferDecision<AbandonRefusal>;
|
|
13
|
+
export declare function decideAbandon({ input, attempt, observation, }: DecideInput<AbandonInput>): OfferDecision<AbandonRefusal>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { activeContract } from "../facts/observation.js";
|
|
2
|
-
export function decideAbandon({ input, attempt, observation }) {
|
|
2
|
+
export function decideAbandon({ input, attempt, observation, }) {
|
|
3
3
|
const id = input.contractId;
|
|
4
4
|
const current = activeContract(observation, id);
|
|
5
5
|
if ("kind" in current)
|
|
@@ -11,4 +11,4 @@ export type AmendRefusal = Readonly<{
|
|
|
11
11
|
kind: "contract-missing" | "terminal" | "terms-moved" | "unknown-prerequisite" | "cyclic-prerequisite";
|
|
12
12
|
contractId: ContractId;
|
|
13
13
|
}>;
|
|
14
|
-
export declare function decideAmend<Failure>({ input, attempt, observation }: DecideInput<AmendInput<Failure>>): OfferDecision<AmendRefusal | Failure>;
|
|
14
|
+
export declare function decideAmend<Failure>({ input, attempt, observation, }: DecideInput<AmendInput<Failure>>): OfferDecision<AmendRefusal | Failure>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { prerequisitesReach, samePrerequisites } from "../facts/eligibility.js";
|
|
2
2
|
import { activeContract, prerequisiteStatus } from "../facts/observation.js";
|
|
3
|
-
export function decideAmend({ input, attempt, observation }) {
|
|
3
|
+
export function decideAmend({ input, attempt, observation, }) {
|
|
4
4
|
const id = input.contractId;
|
|
5
5
|
const current = activeContract(observation, id);
|
|
6
6
|
if ("kind" in current)
|
|
@@ -9,12 +9,12 @@ export function decideAmend({ input, attempt, observation }) {
|
|
|
9
9
|
throw new Error("existing contract requires an amend preparation and source terms");
|
|
10
10
|
}
|
|
11
11
|
const source = input.source;
|
|
12
|
-
const termsCurrent = source.document.key === current.terms.document.key
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
const termsCurrent = source.document.key === current.terms.document.key &&
|
|
13
|
+
source.segments.length === current.terms.segments.length &&
|
|
14
|
+
source.segments.every((value, index) => value === current.terms.segments[index]) &&
|
|
15
|
+
source.gates.length === current.terms.gates.length &&
|
|
16
|
+
source.gates.every((value, index) => value === current.terms.gates[index]) &&
|
|
17
|
+
samePrerequisites(source.after, current.terms.after);
|
|
18
18
|
if (!termsCurrent) {
|
|
19
19
|
return { kind: "refused", refusal: { kind: "terms-moved", contractId: id } };
|
|
20
20
|
}
|
|
@@ -17,5 +17,8 @@ export function decideAttestation({ input, attempt, observation, }) {
|
|
|
17
17
|
...(input.actor === undefined ? {} : { actor: input.actor }),
|
|
18
18
|
data: input.preparation.data,
|
|
19
19
|
};
|
|
20
|
-
return {
|
|
20
|
+
return {
|
|
21
|
+
kind: "offer",
|
|
22
|
+
offer: { facts: [{ contractId: input.contractId, expectedHead: state.head, entries: [attestation] }] },
|
|
23
|
+
};
|
|
21
24
|
}
|
|
@@ -10,4 +10,4 @@ export type BindRefusal = Readonly<{
|
|
|
10
10
|
kind: "contract-exists" | "invalid-after" | "unknown-prerequisite";
|
|
11
11
|
contractId: ContractId;
|
|
12
12
|
}>;
|
|
13
|
-
export declare function decideBind<Failure>({ input, attempt, observation }: DecideInput<BindInput<Failure>>): OfferDecision<BindRefusal | Failure>;
|
|
13
|
+
export declare function decideBind<Failure>({ input, attempt, observation, }: DecideInput<BindInput<Failure>>): OfferDecision<BindRefusal | Failure>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { contractState, prerequisiteStatus } from "../facts/observation.js";
|
|
2
|
-
export function decideBind({ input, attempt, observation }) {
|
|
2
|
+
export function decideBind({ input, attempt, observation, }) {
|
|
3
3
|
const id = input.contractId;
|
|
4
4
|
const current = contractState(observation, id);
|
|
5
5
|
if (current !== null)
|
|
@@ -17,5 +17,5 @@ export type PlacementRefusal = Readonly<{
|
|
|
17
17
|
contractId: ContractId;
|
|
18
18
|
unmet: readonly UnmetPrerequisite[];
|
|
19
19
|
}>;
|
|
20
|
-
export declare function decidePlacement({ input, attempt, observation }: DecideInput<PlacementInput>): OfferDecision<PlacementRefusal>;
|
|
20
|
+
export declare function decidePlacement({ input, attempt, observation, }: DecideInput<PlacementInput>): OfferDecision<PlacementRefusal>;
|
|
21
21
|
export {};
|
|
@@ -16,7 +16,7 @@ function unmetPrerequisites(prerequisites, observation) {
|
|
|
16
16
|
}
|
|
17
17
|
return unmet;
|
|
18
18
|
}
|
|
19
|
-
export function decidePlacement({ input, attempt, observation }) {
|
|
19
|
+
export function decidePlacement({ input, attempt, observation, }) {
|
|
20
20
|
const id = input.contractId;
|
|
21
21
|
const current = activeContract(observation, id);
|
|
22
22
|
if ("kind" in current)
|
|
@@ -46,13 +46,15 @@ export function decidePlacement({ input, attempt, observation }) {
|
|
|
46
46
|
kind: "offer",
|
|
47
47
|
offer: {
|
|
48
48
|
facts: [{ contractId: id, expectedHead: current.head, entries: [claimed] }],
|
|
49
|
-
...(current.coordinates.target === undefined
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
49
|
+
...(current.coordinates.target === undefined
|
|
50
|
+
? {}
|
|
51
|
+
: {
|
|
52
|
+
target: {
|
|
53
|
+
target: current.coordinates.target,
|
|
54
|
+
expectedOid: integration.predecessor,
|
|
55
|
+
newOid: integration.snapshot,
|
|
56
|
+
},
|
|
57
|
+
}),
|
|
56
58
|
},
|
|
57
59
|
};
|
|
58
60
|
}
|