@astrosheep/keiyaku 4.0.1 → 4.0.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.
Files changed (245) hide show
  1. package/README.md +137 -5
  2. package/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +1 -1
  3. package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +1 -1
  4. package/build/integrations/marketplace/plugins/keiyaku/package.json +1 -1
  5. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +3 -3
  6. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +91 -35
  7. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +98 -58
  8. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +152 -53
  9. package/build/src/akuma/abort.d.ts +2 -0
  10. package/build/src/akuma/abort.js +50 -0
  11. package/build/src/akuma/akuma.d.ts +23 -14
  12. package/build/src/akuma/akuma.js +120 -104
  13. package/build/src/akuma/archetype.d.ts +7 -6
  14. package/build/src/akuma/archetype.js +68 -41
  15. package/build/src/akuma/body.d.ts +3 -5
  16. package/build/src/akuma/body.js +408 -213
  17. package/build/src/akuma/heart/facts.d.ts +25 -34
  18. package/build/src/akuma/heart/facts.js +18 -7
  19. package/build/src/akuma/heart/index.d.ts +39 -42
  20. package/build/src/akuma/heart/index.js +134 -93
  21. package/build/src/akuma/heart/rows.d.ts +9 -31
  22. package/build/src/akuma/heart/rows.js +26 -44
  23. package/build/src/akuma/heart/schema.d.ts +1 -1
  24. package/build/src/akuma/heart/schema.js +7 -15
  25. package/build/src/akuma/heart/soul.d.ts +8 -0
  26. package/build/src/akuma/heart/soul.js +136 -0
  27. package/build/src/akuma/heart/storage.d.ts +28 -14
  28. package/build/src/akuma/heart/storage.js +86 -26
  29. package/build/src/akuma/identity.d.ts +2 -2
  30. package/build/src/akuma/identity.js +9 -9
  31. package/build/src/akuma/index.d.ts +6 -1
  32. package/build/src/akuma/index.js +6 -0
  33. package/build/src/akuma/projection.d.ts +117 -38
  34. package/build/src/akuma/projection.js +208 -97
  35. package/build/src/akuma/provider-recipe.d.ts +24 -0
  36. package/build/src/akuma/provider-recipe.js +100 -0
  37. package/build/src/akuma/provider.d.ts +12 -4
  38. package/build/src/akuma/provider.js +117 -70
  39. package/build/src/akuma/providers/acp/core.d.ts +19 -0
  40. package/build/src/akuma/providers/acp/core.js +159 -0
  41. package/build/src/akuma/providers/acp/events.d.ts +20 -0
  42. package/build/src/akuma/providers/acp/events.js +73 -0
  43. package/build/src/akuma/providers/acp/index.d.ts +12 -0
  44. package/build/src/akuma/providers/acp/index.js +94 -0
  45. package/build/src/akuma/providers/claude/events.js +66 -13
  46. package/build/src/akuma/providers/claude/index.d.ts +2 -1
  47. package/build/src/akuma/providers/claude/index.js +35 -21
  48. package/build/src/akuma/providers/codex-app-server/events.js +4 -1
  49. package/build/src/akuma/providers/codex-app-server/index.d.ts +3 -3
  50. package/build/src/akuma/providers/codex-app-server/index.js +24 -31
  51. package/build/src/akuma/providers/grok-build/index.d.ts +4 -0
  52. package/build/src/akuma/providers/grok-build/index.js +70 -0
  53. package/build/src/akuma/providers/index.d.ts +5 -3
  54. package/build/src/akuma/providers/index.js +24 -59
  55. package/build/src/akuma/providers/opencode-sdk/events.d.ts +40 -34
  56. package/build/src/akuma/providers/opencode-sdk/events.js +220 -101
  57. package/build/src/akuma/providers/opencode-sdk/index.d.ts +3 -2
  58. package/build/src/akuma/providers/opencode-sdk/index.js +233 -128
  59. package/build/src/akuma/providers/opencode-sdk/session.d.ts +11 -26
  60. package/build/src/akuma/providers/opencode-sdk/session.js +10 -9
  61. package/build/src/akuma/providers/pi/events.js +49 -11
  62. package/build/src/akuma/providers/pi/index.d.ts +1 -1
  63. package/build/src/akuma/providers/pi/index.js +50 -21
  64. package/build/src/akuma/publication.d.ts +3 -3
  65. package/build/src/akuma/publication.js +28 -40
  66. package/build/src/akuma/requests.d.ts +9 -7
  67. package/build/src/akuma/requests.js +92 -65
  68. package/build/src/alias/index.d.ts +2 -2
  69. package/build/src/alias/index.js +10 -10
  70. package/build/src/body/decode.js +7 -1
  71. package/build/src/body/region.d.ts +2 -0
  72. package/build/src/body/region.js +77 -7
  73. package/build/src/cli/accepted.d.ts +17 -13
  74. package/build/src/cli/accepted.js +72 -34
  75. package/build/src/cli/actor.js +3 -3
  76. package/build/src/cli/commands/akuma-invoke.d.ts +4 -3
  77. package/build/src/cli/commands/akuma-invoke.js +14 -9
  78. package/build/src/cli/commands/akuma.d.ts +13 -3
  79. package/build/src/cli/commands/akuma.js +67 -34
  80. package/build/src/cli/commands/contract.d.ts +18 -5
  81. package/build/src/cli/commands/contract.js +9 -3
  82. package/build/src/cli/commands/task-invoke.d.ts +17 -4
  83. package/build/src/cli/commands/task-invoke.js +43 -20
  84. package/build/src/cli/commands/task-query.d.ts +5 -0
  85. package/build/src/cli/commands/task-query.js +247 -0
  86. package/build/src/cli/commands/task.d.ts +3 -1
  87. package/build/src/cli/commands/task.js +51 -13
  88. package/build/src/cli/coordinates.d.ts +18 -0
  89. package/build/src/cli/coordinates.js +120 -0
  90. package/build/src/cli/draft.d.ts +8 -0
  91. package/build/src/cli/draft.js +96 -0
  92. package/build/src/cli/invoke.d.ts +4 -3
  93. package/build/src/cli/invoke.js +160 -179
  94. package/build/src/cli/main.js +34 -6
  95. package/build/src/cli/parse.d.ts +9 -2
  96. package/build/src/cli/parse.js +29 -7
  97. package/build/src/cli/render/akuma-tool-command.d.ts +6 -0
  98. package/build/src/cli/render/akuma-tool-command.js +144 -0
  99. package/build/src/cli/render/akuma-tool.d.ts +2 -2
  100. package/build/src/cli/render/akuma-tool.js +30 -4
  101. package/build/src/cli/render/akuma.d.ts +1 -0
  102. package/build/src/cli/render/akuma.js +211 -116
  103. package/build/src/cli/render/audit.d.ts +3 -0
  104. package/build/src/cli/render/audit.js +104 -0
  105. package/build/src/cli/render/contract.d.ts +3 -3
  106. package/build/src/cli/render/contract.js +192 -79
  107. package/build/src/cli/render/kanshi.js +263 -168
  108. package/build/src/cli/render/receipt.d.ts +19 -0
  109. package/build/src/cli/render/receipt.js +106 -0
  110. package/build/src/cli/render/refusal.d.ts +16 -2
  111. package/build/src/cli/render/refusal.js +88 -20
  112. package/build/src/cli/render/region.d.ts +2 -0
  113. package/build/src/cli/render/region.js +17 -0
  114. package/build/src/cli/render/task.d.ts +2 -1
  115. package/build/src/cli/render/task.js +223 -60
  116. package/build/src/cli/render/terminal.d.ts +7 -0
  117. package/build/src/cli/render/terminal.js +46 -0
  118. package/build/src/cli/render/text.js +6 -3
  119. package/build/src/cli/result.d.ts +115 -20
  120. package/build/src/cli/usage.d.ts +1 -0
  121. package/build/src/cli/usage.js +3 -0
  122. package/build/src/contract-worktree.d.ts +6 -5
  123. package/build/src/contract-worktree.js +95 -57
  124. package/build/src/coordination/durable-file.d.ts +4 -3
  125. package/build/src/coordination/durable-file.js +29 -25
  126. package/build/src/coordination/sqlite-transaction-lock.d.ts +4 -0
  127. package/build/src/coordination/sqlite-transaction-lock.js +22 -5
  128. package/build/src/core/facts/fold.js +1 -6
  129. package/build/src/core/facts/gate.d.ts +1 -0
  130. package/build/src/core/facts/gate.js +1 -0
  131. package/build/src/core/verbs/amend.d.ts +1 -1
  132. package/build/src/core/verbs/amend.js +0 -11
  133. package/build/src/core/verbs/deliver.d.ts +1 -1
  134. package/build/src/core/verbs/deliver.js +1 -7
  135. package/build/src/core/verbs/placement.d.ts +1 -1
  136. package/build/src/core/verbs/placement.js +4 -1
  137. package/build/src/dispatch/index.d.ts +2 -2
  138. package/build/src/dispatch/index.js +17 -17
  139. package/build/src/git/admission.d.ts +1 -1
  140. package/build/src/git/admission.js +11 -11
  141. package/build/src/git/hooks.js +13 -13
  142. package/build/src/git/integration.d.ts +14 -15
  143. package/build/src/git/integration.js +80 -44
  144. package/build/src/git/observe.d.ts +5 -3
  145. package/build/src/git/observe.js +18 -12
  146. package/build/src/git/read-observation.js +9 -9
  147. package/build/src/git/reconcile.d.ts +2 -0
  148. package/build/src/git/reconcile.js +138 -95
  149. package/build/src/git/repository.d.ts +27 -19
  150. package/build/src/git/repository.js +155 -83
  151. package/build/src/git/scratch.d.ts +7 -3
  152. package/build/src/git/scratch.js +44 -37
  153. package/build/src/git/target-placement.d.ts +40 -4
  154. package/build/src/git/target-placement.js +221 -101
  155. package/build/src/git/tender.d.ts +6 -5
  156. package/build/src/git/tender.js +24 -43
  157. package/build/src/git/terminal-seal.d.ts +1 -1
  158. package/build/src/git/terminal-seal.js +7 -5
  159. package/build/src/git/workspace.d.ts +36 -4
  160. package/build/src/git/workspace.js +53 -18
  161. package/build/src/identity/selector.js +1 -1
  162. package/build/src/index.d.ts +3 -3
  163. package/build/src/index.js +1 -1
  164. package/build/src/kanshi/index.d.ts +2 -2
  165. package/build/src/kanshi/index.js +1 -1
  166. package/build/src/kanshi/read.d.ts +2 -1
  167. package/build/src/kanshi/read.js +118 -27
  168. package/build/src/kanshi/report.d.ts +50 -0
  169. package/build/src/kanshi/select.d.ts +5 -0
  170. package/build/src/kanshi/select.js +32 -0
  171. package/build/src/library/address.d.ts +12 -3
  172. package/build/src/library/address.js +77 -46
  173. package/build/src/library/akuma-creation.d.ts +6 -1
  174. package/build/src/library/akuma-creation.js +86 -13
  175. package/build/src/library/audit.d.ts +18 -0
  176. package/build/src/library/audit.js +44 -0
  177. package/build/src/library/bind.js +4 -4
  178. package/build/src/library/catalog.d.ts +1 -1
  179. package/build/src/library/catalog.js +5 -8
  180. package/build/src/library/contract.d.ts +5 -6
  181. package/build/src/library/contract.js +11 -35
  182. package/build/src/library/delivery.d.ts +2 -1
  183. package/build/src/library/fleet.d.ts +2 -2
  184. package/build/src/library/fleet.js +35 -71
  185. package/build/src/library/keiyaku.d.ts +3 -2
  186. package/build/src/library/keiyaku.js +1 -0
  187. package/build/src/library/mutation.d.ts +7 -6
  188. package/build/src/library/mutation.js +23 -25
  189. package/build/src/library/reconcile.d.ts +29 -0
  190. package/build/src/library/reconcile.js +179 -0
  191. package/build/src/library/region.d.ts +12 -0
  192. package/build/src/library/region.js +15 -1
  193. package/build/src/library/repo.d.ts +4 -16
  194. package/build/src/library/repo.js +13 -38
  195. package/build/src/protocol/attempt.js +6 -6
  196. package/build/src/protocol/bind.js +4 -4
  197. package/build/src/protocol/intent.d.ts +15 -2
  198. package/build/src/protocol/intent.js +21 -6
  199. package/build/src/protocol/operations.d.ts +73 -22
  200. package/build/src/protocol/operations.js +292 -88
  201. package/build/src/protocol/outcome.d.ts +9 -3
  202. package/build/src/protocol/outcome.js +3 -1
  203. package/build/src/protocol/placement.d.ts +15 -4
  204. package/build/src/protocol/placement.js +58 -25
  205. package/build/src/protocol/read/status.d.ts +8 -1
  206. package/build/src/protocol/read/status.js +61 -14
  207. package/build/src/runtime/proc/line-rpc.d.ts +3 -8
  208. package/build/src/runtime/proc/line-rpc.js +16 -43
  209. package/build/src/runtime/proc/run.d.ts +15 -29
  210. package/build/src/runtime/proc/run.js +113 -105
  211. package/build/src/runtime/proc/stdio.d.ts +18 -0
  212. package/build/src/runtime/proc/stdio.js +58 -0
  213. package/build/src/settings.d.ts +2 -2
  214. package/build/src/settings.js +8 -8
  215. package/build/src/settlement/fence.d.ts +0 -4
  216. package/build/src/settlement/fence.js +0 -9
  217. package/build/src/settlement/holder.d.ts +11 -1
  218. package/build/src/settlement/holder.js +25 -0
  219. package/build/src/settlement/settle.d.ts +1 -1
  220. package/build/src/settlement/settle.js +52 -89
  221. package/build/src/task/board.d.ts +3 -3
  222. package/build/src/task/board.js +6 -9
  223. package/build/src/task/compose.d.ts +1 -1
  224. package/build/src/task/compose.js +18 -10
  225. package/build/src/task/context.d.ts +3 -3
  226. package/build/src/task/context.js +14 -14
  227. package/build/src/task/document.d.ts +2 -1
  228. package/build/src/task/document.js +15 -5
  229. package/build/src/task/index.d.ts +19 -7
  230. package/build/src/task/index.js +42 -23
  231. package/build/src/task/operations.d.ts +15 -9
  232. package/build/src/task/operations.js +78 -48
  233. package/build/src/task/query.d.ts +67 -0
  234. package/build/src/task/query.js +279 -0
  235. package/build/src/task/store.d.ts +2 -2
  236. package/build/src/task/store.js +40 -25
  237. package/build/src/verification/execution.d.ts +4 -2
  238. package/build/src/verification/execution.js +9 -5
  239. package/build/src/workspace-place.d.ts +41 -0
  240. package/build/src/workspace-place.js +386 -0
  241. package/build/src/world.d.ts +13 -2
  242. package/build/src/world.js +81 -48
  243. package/package.json +3 -2
  244. package/build/src/protocol/read/audit.d.ts +0 -31
  245. package/build/src/protocol/read/audit.js +0 -55
@@ -1,6 +1,15 @@
1
- import type { AuditReport, ChangeId, ContractId, Fact, PlacementStop, ReconcileReport, RegionOverlap, Review, SettlementReport, SnapshotId, VerificationStop } from "../index.js";
1
+ import type { AuditReport, ContractId, Fact, MutationResult, PlacementStop, ReconcileReport, Review, VerificationReuse, VerificationStop } from "../index.js";
2
2
  import type { KanshiReport } from "../kanshi/index.js";
3
+ import type { RegionRead, Section } from "../kanshi/index.js";
3
4
  import type { Catalog } from "../index.js";
5
+ import type { RegionObservation } from "../library/region.js";
6
+ export type BindDraftReceipt = Readonly<{
7
+ path: string;
8
+ warning?: string;
9
+ } | {
10
+ path?: never;
11
+ warning: string;
12
+ }>;
4
13
  export type Effect = ReconcileReport["effects"][number];
5
14
  export type AcceptedFact = Readonly<{
6
15
  contract: ContractId;
@@ -8,36 +17,117 @@ export type AcceptedFact = Readonly<{
8
17
  kind: Fact["kind"];
9
18
  }>;
10
19
  export type Lag = ReconcileReport["lag"][number];
11
- export type DiffUnavailable = Readonly<{
12
- reason: "git-unavailable";
13
- integrationSnapshot: SnapshotId;
14
- changeId: ChangeId;
15
- }>;
16
- export type AcceptedResult = Readonly<{
20
+ type MutationEnvelope = Pick<MutationResult<unknown>, "head" | "effects" | "settlement">;
21
+ export type AcceptedEnvelope = Readonly<{
17
22
  kind: "accepted";
18
- verb: string;
19
23
  contract: ContractId;
20
- head: string | null;
24
+ head: MutationEnvelope["head"];
21
25
  facts: readonly AcceptedFact[];
22
- effects: readonly Effect[];
23
- settlement: SettlementReport;
24
- target?: string | null;
26
+ effects: MutationEnvelope["effects"];
27
+ settlement: MutationEnvelope["settlement"];
28
+ lag?: readonly [Lag, ...Lag[]];
29
+ }>;
30
+ export type AcceptedBindResult = AcceptedEnvelope & Readonly<{
31
+ verb: "bind";
32
+ target: string | null;
33
+ verification?: never;
34
+ verificationReuse?: never;
35
+ placement?: never;
36
+ cleanup?: never;
37
+ leak?: never;
38
+ report?: never;
39
+ workspace?: never;
40
+ diff?: never;
41
+ }> & RegionObservation;
42
+ export type AcceptedAmendResult = AcceptedEnvelope & Readonly<{
43
+ verb: "amend";
44
+ diff: string;
45
+ target?: never;
46
+ verification?: never;
47
+ verificationReuse?: never;
48
+ placement?: never;
49
+ cleanup?: never;
50
+ leak?: never;
51
+ report?: never;
52
+ workspace?: never;
53
+ }> & RegionObservation;
54
+ export type AcceptedDeliverResult = AcceptedEnvelope & Readonly<{
55
+ verb: "deliver";
25
56
  verification?: VerificationStop;
57
+ verificationReuse?: VerificationReuse;
58
+ placement?: PlacementStop;
59
+ cleanup?: MutationResult<unknown>["cleanup"];
60
+ leak?: MutationResult<unknown>["leak"];
61
+ target?: never;
62
+ overlaps?: never;
63
+ overlapFailure?: never;
64
+ report?: never;
65
+ workspace?: never;
66
+ diff?: never;
67
+ }>;
68
+ export type AcceptedReviewResult = AcceptedEnvelope & Readonly<{
69
+ verb: "review";
26
70
  placement?: PlacementStop;
27
- cleanup?: NonNullable<AuditReport["cleanup"]>;
28
- leak?: NonNullable<AuditReport["leak"]>;
29
- overlaps?: readonly RegionOverlap[];
30
- overlapFailure?: string;
31
- report?: AuditReport;
32
71
  workspace?: Review["workspace"];
33
- diff?: string | DiffUnavailable;
34
- lag?: readonly Lag[];
72
+ target?: never;
73
+ verification?: never;
74
+ verificationReuse?: never;
75
+ cleanup?: never;
76
+ leak?: never;
77
+ overlaps?: never;
78
+ overlapFailure?: never;
79
+ report?: never;
80
+ diff?: never;
81
+ }>;
82
+ export type AcceptedArcResult = AcceptedEnvelope & Readonly<{
83
+ verb: "arc";
84
+ target?: never;
85
+ verification?: never;
86
+ verificationReuse?: never;
87
+ placement?: never;
88
+ cleanup?: never;
89
+ leak?: never;
90
+ overlaps?: never;
91
+ overlapFailure?: never;
92
+ report?: never;
93
+ workspace?: never;
94
+ diff?: never;
95
+ }>;
96
+ export type AcceptedAbandonResult = AcceptedEnvelope & Readonly<{
97
+ verb: "abandon";
98
+ target?: never;
99
+ verification?: never;
100
+ verificationReuse?: never;
101
+ placement?: never;
102
+ cleanup?: never;
103
+ leak?: never;
104
+ overlaps?: never;
105
+ overlapFailure?: never;
106
+ report?: never;
107
+ workspace?: never;
108
+ diff?: never;
35
109
  }>;
110
+ export type AcceptedAuditResult = AcceptedEnvelope & Readonly<{
111
+ verb: "audit";
112
+ report: AuditReport;
113
+ cleanup?: MutationResult<unknown>["cleanup"];
114
+ leak?: MutationResult<unknown>["leak"];
115
+ target?: never;
116
+ verification?: never;
117
+ verificationReuse?: never;
118
+ placement?: never;
119
+ overlaps?: never;
120
+ overlapFailure?: never;
121
+ workspace?: never;
122
+ diff?: never;
123
+ }>;
124
+ export type AcceptedResult = AcceptedBindResult | AcceptedAmendResult | AcceptedDeliverResult | AcceptedReviewResult | AcceptedArcResult | AcceptedAbandonResult | AcceptedAuditResult;
36
125
  export type RefusedResult = Readonly<{
37
126
  kind: "refused";
38
127
  verb: string;
39
128
  contract?: ContractId;
40
129
  refusal: unknown;
130
+ draft?: BindDraftReceipt;
41
131
  }>;
42
132
  export type RetryResult = Readonly<{
43
133
  kind: "retry";
@@ -60,8 +150,13 @@ export type StatusResult = Readonly<{
60
150
  report: KanshiReport;
61
151
  selection: "world" | "contract";
62
152
  }>;
153
+ export type RegionResult = Readonly<{
154
+ kind: "region";
155
+ region: Section<RegionRead>;
156
+ }>;
63
157
  export type CatalogResult = Readonly<{
64
158
  kind: "catalog";
65
159
  catalog: Catalog;
66
160
  }>;
67
- export type InvocationResult = AcceptedResult | RefusedResult | RetryResult | ObservationResult | GuidanceResult | StatusResult | CatalogResult;
161
+ export type InvocationResult = AcceptedResult | RefusedResult | RetryResult | ObservationResult | GuidanceResult | StatusResult | RegionResult | CatalogResult;
162
+ export {};
@@ -3,4 +3,5 @@ export declare class CliUsageError extends Error {
3
3
  readonly projection?: string | undefined;
4
4
  constructor(diagnostic: string, projection?: string | undefined);
5
5
  }
6
+ export declare function isBlankInput(value: string): boolean;
6
7
  export declare function usageLine(usage: string): string;
@@ -8,6 +8,9 @@ export class CliUsageError extends Error {
8
8
  this.name = "CliUsageError";
9
9
  }
10
10
  }
11
+ export function isBlankInput(value) {
12
+ return value.trim().length === 0;
13
+ }
11
14
  export function usageLine(usage) {
12
15
  return usage.split("\n").map((line, index) => {
13
16
  if (index === 0)
@@ -1,6 +1,7 @@
1
1
  import { type DerivedFileAction } from "./coordination/durable-file.js";
2
2
  import { type ContractId, type ContractState } from "./core/facts/types.js";
3
3
  import { type GitRepository } from "./git/repository.js";
4
+ import { type PlaceRegister } from "./workspace-place.js";
4
5
  export declare function renderContractGuidance(state: ContractState): string;
5
6
  export type ContractFileEffect = Readonly<{
6
7
  kind: "contract-file";
@@ -28,7 +29,7 @@ export type ContractAppointment = Readonly<{
28
29
  kind: "invalid";
29
30
  path: string;
30
31
  }>;
31
- export declare function readContractAppointment(repository: GitRepository): ContractAppointment;
32
+ export declare function readContractAppointment(repository: GitRepository): Promise<ContractAppointment>;
32
33
  export type ContractReservation = Readonly<{
33
34
  kind: "reserved";
34
35
  path: string;
@@ -36,8 +37,8 @@ export type ContractReservation = Readonly<{
36
37
  kind: "absent";
37
38
  path: string;
38
39
  }>>;
39
- export declare function reserveContractWorktree(repository: GitRepository, contract: ContractId): ContractReservation;
40
+ export declare function reserveContractWorktree(repository: GitRepository, contract: ContractId): Promise<ContractReservation>;
40
41
  export declare function withContractWorktreeAppointment<T>(repository: GitRepository, action: () => T | Promise<T>): Promise<T>;
41
- export declare function releaseContractWorktree(repository: GitRepository, contract: ContractId): void;
42
- export declare function removeContractWorktreeAppointment(repository: GitRepository, contract: ContractId): void;
43
- export declare function projectContractWorktree(repository: GitRepository, state: ContractState | null): ContractWorktreeResult;
42
+ export declare function releaseContractWorktree(repository: GitRepository, contract: ContractId): Promise<void>;
43
+ export declare function removeContractWorktreeAppointment(repository: GitRepository, contract: ContractId): Promise<void>;
44
+ export declare function projectContractWorktree(repository: GitRepository, state: ContractState | null, register?: PlaceRegister): Promise<ContractWorktreeResult>;
@@ -1,13 +1,35 @@
1
- import { chmodSync, lstatSync, readFileSync, unlinkSync } from "node:fs";
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
4
  import { createFileDurablyExclusive, repairDerivedFile, } from "./coordination/durable-file.js";
5
5
  import { contractId } from "./core/facts/types.js";
6
6
  import { GitPlumbingError, runGit, worktreeGitDirectory, worktreeRoot, } from "./git/repository.js";
7
- import { deliveryWorktreePath } from "./git/workspace.js";
7
+ import { worktreePath } from "./git/workspace.js";
8
+ import { appointmentFor, placeRegisterPath, readPlaceRegister, } from "./workspace-place.js";
8
9
  const IGNORE_BYTES = ".gitignore\nKEIYAKU.md\n";
10
+ const APPOINTMENT_DESCRIPTION = "This is a read-only projection. Do not edit manually.";
9
11
  function appointmentBytes(contract) {
10
- return `---\ncontract: ${contract}\n---\n`;
12
+ return `---\ncontract: ${contract}\ndescription: ${APPOINTMENT_DESCRIPTION}\n---\n`;
13
+ }
14
+ function appointedContract(bytes) {
15
+ const lines = bytes.split(/\r?\n/u);
16
+ if (lines[0] !== "---")
17
+ return undefined;
18
+ const close = lines.indexOf("---", 1);
19
+ if (close !== 2 && close !== 3)
20
+ return undefined;
21
+ const identity = lines[1];
22
+ const description = lines[2];
23
+ if (identity === undefined || !identity.startsWith("contract: "))
24
+ return undefined;
25
+ if (close === 3 && (description === undefined || !description.startsWith("description: ")))
26
+ return undefined;
27
+ try {
28
+ return contractId(identity.slice("contract: ".length));
29
+ }
30
+ catch {
31
+ return undefined;
32
+ }
11
33
  }
12
34
  function renderArc(arc) {
13
35
  return [
@@ -25,10 +47,12 @@ const FULFILLMENT = [
25
47
  "Read the complete Contract before acting and keep work inside that worktree.", "",
26
48
  "### Deliverer", "",
27
49
  "Implement and verify the Objective under the Design, Region, and Criteria in this Contract.",
50
+ "When an Arc is active, stay within that current chapter.",
28
51
  "Leave lifecycle decisions to the caller; report the candidate, verification performed, and any unmet term.", "",
29
52
  "### Reviewer", "",
30
- "Review the current candidate against the complete Contract and report material findings with direct evidence.",
31
- "Do not modify the candidate. State which Criteria are covered and identify missing or unsupported evidence.",
53
+ "Review this Contract worktree against the complete Contract.",
54
+ "Review the complete current worktree snapshot, not a named candidate commit.",
55
+ "Do not modify it; report covered Criteria, findings, and missing evidence.",
32
56
  ].join("\n");
33
57
  export function renderContractGuidance(state) {
34
58
  return [
@@ -38,9 +62,9 @@ export function renderContractGuidance(state) {
38
62
  FULFILLMENT,
39
63
  ].join("\n\n").concat("\n");
40
64
  }
41
- function isTracked(repository, relativePath) {
65
+ async function isTracked(repository, relativePath) {
42
66
  try {
43
- runGit(repository, ["ls-files", "--error-unmatch", "--", relativePath]);
67
+ await runGit(repository, ["ls-files", "--error-unmatch", "--", relativePath]);
44
68
  return true;
45
69
  }
46
70
  catch (error) {
@@ -52,42 +76,39 @@ function isTracked(repository, relativePath) {
52
76
  function generatedPath(worktree, name) {
53
77
  return join(worktree, ".keiyaku", name);
54
78
  }
55
- export function readContractAppointment(repository) {
56
- const path = generatedPath(worktreeRoot(repository), "KEIYAKU.md");
57
- const stat = lstatSync(path, { throwIfNoEntry: false });
79
+ export async function readContractAppointment(repository) {
80
+ const path = generatedPath(await worktreeRoot(repository), "KEIYAKU.md");
81
+ const stat = await lstat(path).catch((error) => {
82
+ if (error.code === "ENOENT")
83
+ return undefined;
84
+ throw error;
85
+ });
58
86
  if (stat === undefined)
59
87
  return { kind: "absent", path };
60
88
  if (!stat.isFile() || stat.isSymbolicLink())
61
89
  return { kind: "invalid", path };
62
- const match = /^---\r?\ncontract: ([^\r\n]+)\r?\n---(?:\r?\n|$)/u.exec(readFileSync(path, "utf8"));
63
- if (match?.[1] === undefined)
64
- return { kind: "invalid", path };
65
- try {
66
- return { kind: "appointed", path, contract: contractId(match[1]) };
67
- }
68
- catch {
69
- return { kind: "invalid", path };
70
- }
90
+ const contract = appointedContract(await readFile(path, "utf8"));
91
+ return contract === undefined ? { kind: "invalid", path } : { kind: "appointed", path, contract };
71
92
  }
72
- export function reserveContractWorktree(repository, contract) {
73
- const worktree = worktreeRoot(repository);
93
+ export async function reserveContractWorktree(repository, contract) {
94
+ const worktree = await worktreeRoot(repository);
74
95
  const scoped = { ...repository, effectiveCwd: worktree };
75
96
  const ignore = generatedPath(worktree, ".gitignore");
76
- if (isTracked(scoped, ".keiyaku/.gitignore"))
97
+ if (await isTracked(scoped, ".keiyaku/.gitignore"))
77
98
  throw new Error(`generated path is tracked by Git: ${ignore}`);
78
- repairDerivedFile(ignore, IGNORE_BYTES);
99
+ await repairDerivedFile(ignore, IGNORE_BYTES);
79
100
  const path = generatedPath(worktree, "KEIYAKU.md");
80
- if (isTracked(scoped, ".keiyaku/KEIYAKU.md"))
101
+ if (await isTracked(scoped, ".keiyaku/KEIYAKU.md"))
81
102
  throw new Error(`generated path is tracked by Git: ${path}`);
82
- if (createFileDurablyExclusive(path, appointmentBytes(contract), 0o444))
103
+ if (await createFileDurablyExclusive(path, appointmentBytes(contract), 0o444))
83
104
  return { kind: "reserved", path };
84
- const appointment = readContractAppointment(repository);
105
+ const appointment = await readContractAppointment(repository);
85
106
  return appointment.kind === "absent" ? { kind: "invalid", path } : appointment;
86
107
  }
87
108
  export async function withContractWorktreeAppointment(repository, action) {
88
- const root = worktreeRoot(repository);
109
+ const root = await worktreeRoot(repository);
89
110
  const lock = await acquireSqliteTransactionLock({
90
- path: join(worktreeGitDirectory(repository, root), "keiyaku", "contract-worktree.sqlite"),
111
+ path: join(await worktreeGitDirectory(repository, root), "keiyaku", "contract-worktree.sqlite"),
91
112
  mode: "immediate",
92
113
  });
93
114
  try {
@@ -97,38 +118,42 @@ export async function withContractWorktreeAppointment(repository, action) {
97
118
  lock.close();
98
119
  }
99
120
  }
100
- export function releaseContractWorktree(repository, contract) {
101
- const appointment = readContractAppointment(repository);
121
+ export async function releaseContractWorktree(repository, contract) {
122
+ const appointment = await readContractAppointment(repository);
102
123
  if (appointment.kind !== "appointed" || appointment.contract !== contract)
103
124
  return;
104
- if (readFileSync(appointment.path, "utf8") !== appointmentBytes(contract))
125
+ if (await readFile(appointment.path, "utf8") !== appointmentBytes(contract))
105
126
  return;
106
127
  try {
107
- unlinkSync(appointment.path);
128
+ await unlink(appointment.path);
108
129
  }
109
130
  catch { /* reservation cleanup is best effort */ }
110
131
  }
111
- export function removeContractWorktreeAppointment(repository, contract) {
112
- const appointment = readContractAppointment(repository);
132
+ export async function removeContractWorktreeAppointment(repository, contract) {
133
+ const appointment = await readContractAppointment(repository);
113
134
  if (appointment.kind !== "appointed" || appointment.contract !== contract)
114
135
  return;
115
- unlinkSync(appointment.path);
136
+ await unlink(appointment.path);
116
137
  }
117
- function repair(repository, worktree, relativePath, bytes) {
138
+ async function repair(repository, worktree, relativePath, bytes) {
118
139
  const path = join(worktree, relativePath);
119
- if (isTracked(repository, relativePath))
140
+ if (await isTracked(repository, relativePath))
120
141
  throw new Error(`generated path is tracked by Git: ${path}`);
121
- const action = repairDerivedFile(path, bytes);
142
+ const action = await repairDerivedFile(path, bytes);
122
143
  if (relativePath === ".keiyaku/KEIYAKU.md") {
123
144
  try {
124
- chmodSync(path, 0o444);
145
+ await chmod(path, 0o444);
125
146
  }
126
147
  catch { /* advisory protection only */ }
127
148
  }
128
149
  return { kind: "contract-file", path, action };
129
150
  }
130
- function materialize(repository, worktree, guidance) {
131
- const stat = lstatSync(worktree, { throwIfNoEntry: false });
151
+ async function materialize(repository, worktree, guidance) {
152
+ const stat = await lstat(worktree).catch((error) => {
153
+ if (error.code === "ENOENT")
154
+ return undefined;
155
+ throw error;
156
+ });
132
157
  if (stat === undefined || !stat.isDirectory()) {
133
158
  return {
134
159
  effects: [],
@@ -147,7 +172,7 @@ function materialize(repository, worktree, guidance) {
147
172
  [".keiyaku/KEIYAKU.md", guidance],
148
173
  ]) {
149
174
  try {
150
- effects.push(repair(scoped, worktree, relativePath, bytes));
175
+ effects.push(await repair(scoped, worktree, relativePath, bytes));
151
176
  }
152
177
  catch (error) {
153
178
  return {
@@ -163,11 +188,11 @@ function materialize(repository, worktree, guidance) {
163
188
  }
164
189
  return { effects, lag: [] };
165
190
  }
166
- function here(repository, state, guidance) {
167
- const worktree = worktreeRoot(repository);
168
- const appointment = readContractAppointment(repository);
191
+ async function here(repository, state, guidance) {
192
+ const worktree = await worktreeRoot(repository);
193
+ const appointment = await readContractAppointment(repository);
169
194
  if (appointment.kind === "appointed" && appointment.contract === state.id) {
170
- return materialize(repository, worktree, guidance);
195
+ return await materialize(repository, worktree, guidance);
171
196
  }
172
197
  const diagnostic = appointment.kind === "appointed"
173
198
  ? `here worktree is appointed to ${appointment.contract}`
@@ -177,9 +202,9 @@ function here(repository, state, guidance) {
177
202
  lag: [{ kind: "contract-file-failed", worktree, path: appointment.path, diagnostic }],
178
203
  };
179
204
  }
180
- function removeHere(repository, contract) {
181
- const worktree = worktreeRoot(repository);
182
- const appointment = readContractAppointment(repository);
205
+ async function removeHere(repository, contract) {
206
+ const worktree = await worktreeRoot(repository);
207
+ const appointment = await readContractAppointment(repository);
183
208
  if (appointment.kind === "absent" || (appointment.kind === "appointed" && appointment.contract !== contract)) {
184
209
  return { effects: [], lag: [] };
185
210
  }
@@ -189,7 +214,7 @@ function removeHere(repository, contract) {
189
214
  }] };
190
215
  }
191
216
  try {
192
- unlinkSync(appointment.path);
217
+ await unlink(appointment.path);
193
218
  return { effects: [{ kind: "contract-file", path: appointment.path, action: "removed" }], lag: [] };
194
219
  }
195
220
  catch (error) {
@@ -201,13 +226,26 @@ function removeHere(repository, contract) {
201
226
  }] };
202
227
  }
203
228
  }
204
- export function projectContractWorktree(repository, state) {
205
- if (state === null)
206
- return { effects: [], lag: [] };
229
+ export async function projectContractWorktree(repository, state, register) {
230
+ if (state === null || state.terminal) {
231
+ return state?.coordinates.workspace === "here"
232
+ ? await removeHere(repository, state.id)
233
+ : { effects: [], lag: [] };
234
+ }
207
235
  if (state.coordinates.workspace === "here") {
208
- return state.terminal ? removeHere(repository, state.id) : here(repository, state, renderContractGuidance(state));
236
+ return await here(repository, state, renderContractGuidance(state));
237
+ }
238
+ const appointed = appointmentFor(register ?? await readPlaceRegister(repository), state.id);
239
+ if (appointed === undefined) {
240
+ return {
241
+ effects: [],
242
+ lag: [{
243
+ kind: "contract-file-failed",
244
+ worktree: repository.primaryWorktree,
245
+ path: placeRegisterPath(repository),
246
+ diagnostic: "managed Contract is unappointed",
247
+ }],
248
+ };
209
249
  }
210
- return state.terminal
211
- ? { effects: [], lag: [] }
212
- : materialize(repository, deliveryWorktreePath(repository, state.id), renderContractGuidance(state));
250
+ return await materialize(repository, worktreePath(repository, appointed.place), renderContractGuidance(state));
213
251
  }
@@ -1,4 +1,5 @@
1
- export declare function replaceFileDurably(path: string, bytes: string | Uint8Array): void;
1
+ export declare function syncDirectory(path: string): void;
2
+ export declare function replaceFileDurably(path: string, bytes: string | Uint8Array): Promise<void>;
2
3
  export type DerivedFileAction = "created" | "updated" | "unchanged";
3
- export declare function createFileDurablyExclusive(path: string, bytes: string | Uint8Array, mode?: number): boolean;
4
- export declare function repairDerivedFile(path: string, bytes: string | Uint8Array): DerivedFileAction;
4
+ export declare function createFileDurablyExclusive(path: string, bytes: string | Uint8Array, mode?: number): Promise<boolean>;
5
+ export declare function repairDerivedFile(path: string, bytes: string | Uint8Array): Promise<DerivedFileAction>;
@@ -1,7 +1,19 @@
1
1
  import { randomBytes } from "node:crypto";
2
- import { closeSync, fsyncSync, lstatSync, mkdirSync, openSync, readFileSync, renameSync, unlinkSync, writeFileSync, } from "node:fs";
2
+ import { closeSync, fsyncSync, openSync, renameSync, writeFileSync, } from "node:fs";
3
+ import { lstat, mkdir, readFile, unlink } from "node:fs/promises";
3
4
  import { dirname, resolve } from "node:path";
4
- export function replaceFileDurably(path, bytes) {
5
+ export function syncDirectory(path) {
6
+ if (process.platform === "win32")
7
+ return;
8
+ const directory = openSync(path, "r");
9
+ try {
10
+ fsyncSync(directory);
11
+ }
12
+ finally {
13
+ closeSync(directory);
14
+ }
15
+ }
16
+ export async function replaceFileDurably(path, bytes) {
5
17
  const parent = dirname(path);
6
18
  for (;;) {
7
19
  const temporary = resolve(parent, `.tmp-${randomBytes(8).toString("hex")}`);
@@ -13,20 +25,14 @@ export function replaceFileDurably(path, bytes) {
13
25
  closeSync(descriptor);
14
26
  descriptor = undefined;
15
27
  renameSync(temporary, path);
16
- const directory = openSync(parent, "r");
17
- try {
18
- fsyncSync(directory);
19
- }
20
- finally {
21
- closeSync(directory);
22
- }
28
+ syncDirectory(parent);
23
29
  return;
24
30
  }
25
31
  catch (error) {
26
32
  if (descriptor !== undefined)
27
33
  closeSync(descriptor);
28
34
  try {
29
- unlinkSync(temporary);
35
+ await unlink(temporary);
30
36
  }
31
37
  catch { /* renamed, absent, or best-effort cleanup */ }
32
38
  if (error.code !== "EEXIST")
@@ -34,9 +40,9 @@ export function replaceFileDurably(path, bytes) {
34
40
  }
35
41
  }
36
42
  }
37
- export function createFileDurablyExclusive(path, bytes, mode = 0o600) {
43
+ export async function createFileDurablyExclusive(path, bytes, mode = 0o600) {
38
44
  const parent = dirname(path);
39
- mkdirSync(parent, { recursive: true });
45
+ await mkdir(parent, { recursive: true });
40
46
  let descriptor;
41
47
  try {
42
48
  descriptor = openSync(path, "wx", mode);
@@ -44,13 +50,7 @@ export function createFileDurablyExclusive(path, bytes, mode = 0o600) {
44
50
  fsyncSync(descriptor);
45
51
  closeSync(descriptor);
46
52
  descriptor = undefined;
47
- const directory = openSync(parent, "r");
48
- try {
49
- fsyncSync(directory);
50
- }
51
- finally {
52
- closeSync(directory);
53
- }
53
+ syncDirectory(parent);
54
54
  return true;
55
55
  }
56
56
  catch (error) {
@@ -59,21 +59,25 @@ export function createFileDurablyExclusive(path, bytes, mode = 0o600) {
59
59
  if (error.code === "EEXIST")
60
60
  return false;
61
61
  try {
62
- unlinkSync(path);
62
+ await unlink(path);
63
63
  }
64
64
  catch { /* absent or best-effort cleanup */ }
65
65
  throw error;
66
66
  }
67
67
  }
68
- export function repairDerivedFile(path, bytes) {
68
+ export async function repairDerivedFile(path, bytes) {
69
69
  const expected = Buffer.from(bytes);
70
- mkdirSync(dirname(path), { recursive: true });
71
- const stat = lstatSync(path, { throwIfNoEntry: false });
70
+ await mkdir(dirname(path), { recursive: true });
71
+ const stat = await lstat(path).catch((error) => {
72
+ if (error.code === "ENOENT")
73
+ return undefined;
74
+ throw error;
75
+ });
72
76
  if (stat !== undefined && (!stat.isFile() || stat.isSymbolicLink())) {
73
77
  throw new Error(`derived file is not a regular file: ${path}`);
74
78
  }
75
- if (stat !== undefined && readFileSync(path).equals(expected))
79
+ if (stat !== undefined && (await readFile(path)).equals(expected))
76
80
  return "unchanged";
77
- replaceFileDurably(path, expected);
81
+ await replaceFileDurably(path, expected);
78
82
  return stat === undefined ? "created" : "updated";
79
83
  }
@@ -12,3 +12,7 @@ export declare function acquireSqliteTransactionLock(input: {
12
12
  timeoutMs?: number;
13
13
  signal?: AbortSignal;
14
14
  }): Promise<HeldSqliteTransactionLock>;
15
+ export declare function tryAcquireSqliteTransactionLock(input: {
16
+ path: string;
17
+ mode: SqliteTransactionLockMode;
18
+ }): Promise<HeldSqliteTransactionLock | null>;
@@ -1,5 +1,5 @@
1
1
  import { randomInt } from "node:crypto";
2
- import { lstatSync, mkdirSync } from "node:fs";
2
+ import { lstat, mkdir } from "node:fs/promises";
3
3
  import { dirname } from "node:path";
4
4
  import { DatabaseSync } from "node:sqlite";
5
5
  const SQLITE_BUSY = 5;
@@ -41,13 +41,17 @@ function closeDatabase(database) {
41
41
  }
42
42
  return failure;
43
43
  }
44
- function openLock(path, mode) {
44
+ async function openLock(path, mode) {
45
45
  try {
46
- const stat = lstatSync(path, { throwIfNoEntry: false });
46
+ const stat = await lstat(path).catch((error) => {
47
+ if (error.code === "ENOENT")
48
+ return undefined;
49
+ throw error;
50
+ });
47
51
  if (stat && (!stat.isFile() || stat.isSymbolicLink())) {
48
52
  throw new SqliteTransactionLockError(`SQLite lock is not a regular file: ${path}`, "invalid");
49
53
  }
50
- mkdirSync(dirname(path), { recursive: true });
54
+ await mkdir(dirname(path), { recursive: true });
51
55
  }
52
56
  catch (error) {
53
57
  if (error instanceof SqliteTransactionLockError)
@@ -101,7 +105,7 @@ export async function acquireSqliteTransactionLock(input) {
101
105
  for (;;) {
102
106
  input.signal?.throwIfAborted();
103
107
  try {
104
- return openLock(input.path, input.mode);
108
+ return await openLock(input.path, input.mode);
105
109
  }
106
110
  catch (error) {
107
111
  if (!isBusy(error))
@@ -117,3 +121,16 @@ export async function acquireSqliteTransactionLock(input) {
117
121
  }
118
122
  }
119
123
  }
124
+ export async function tryAcquireSqliteTransactionLock(input) {
125
+ if (input.path.length === 0) {
126
+ throw new SqliteTransactionLockError("SQLite lock path must be valid", "invalid");
127
+ }
128
+ try {
129
+ return await openLock(input.path, input.mode);
130
+ }
131
+ catch (error) {
132
+ if (isBusy(error))
133
+ return null;
134
+ throw error;
135
+ }
136
+ }