@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
@@ -3,7 +3,7 @@ import { type PlacementRefusal } from "../core/verbs/placement.js";
3
3
  import type { GitDecodeChannel } from "../git/read-observation.js";
4
4
  import { type ReconcileResult } from "../git/reconcile.js";
5
5
  import { type GitRepository } from "../git/repository.js";
6
- import { type TargetPlacementRefusal } from "../git/target-placement.js";
6
+ import { type TargetPlacementObservationInput, type TargetPlacementRefusal } from "../git/target-placement.js";
7
7
  import { type AcceptedAdmission } from "./attempt.js";
8
8
  import { type ProtocolResult } from "./run.js";
9
9
  export type PlacementExecutionFailure = Readonly<{
@@ -17,14 +17,25 @@ export type TargetMovedStop = Readonly<{
17
17
  expected: SnapshotId;
18
18
  observed: SnapshotId | null;
19
19
  }>;
20
- export type PlacementProtocolResult = (AcceptedAdmission & Readonly<{
20
+ export type PlacementProtocolResult<ExtraRefusal = never> = (AcceptedAdmission & Readonly<{
21
21
  physical?: ReconcileResult;
22
- }>) | Exclude<ProtocolResult<PlacementRefusal | TargetPlacementRefusal>, AcceptedAdmission> | TargetMovedStop | PlacementExecutionFailure;
22
+ }>) | Exclude<ProtocolResult<PlacementRefusal | TargetPlacementRefusal | ExtraRefusal>, AcceptedAdmission> | TargetMovedStop | PlacementExecutionFailure;
23
23
  type PlacementProtocolInput = Readonly<{
24
24
  contractId: ContractId;
25
25
  actor?: ActorId;
26
26
  at: string;
27
27
  }>;
28
+ export type ProspectiveTargetPreview = Readonly<{
29
+ kind: "ready";
30
+ }> | Readonly<{
31
+ kind: "refused";
32
+ refusal: TargetPlacementRefusal;
33
+ }> | Readonly<{
34
+ kind: "failed";
35
+ diagnostic: string;
36
+ }>;
37
+ /** Observe prospective target followability without fencing or placing. */
38
+ export declare function observeProspectiveTargetPlacement(repository: GitRepository, input: TargetPlacementObservationInput): Promise<ProspectiveTargetPreview>;
28
39
  /** Run the sole placement adjudicator inside the target's physical fence. */
29
- export declare function admitPlacement(channel: GitDecodeChannel, repository: GitRepository, target: string | undefined, input: PlacementProtocolInput): Promise<PlacementProtocolResult>;
40
+ export declare function admitPlacement<ExtraRefusal = never>(channel: GitDecodeChannel, repository: GitRepository, target: string | undefined, input: PlacementProtocolInput, onDeliveryMissing?: () => Promise<PlacementProtocolResult<ExtraRefusal> | undefined>): Promise<PlacementProtocolResult<ExtraRefusal>>;
30
41
  export {};
@@ -2,19 +2,36 @@ import { SqliteTransactionLockError } from "../coordination/sqlite-transaction-l
2
2
  import { AuthorityCorruptionError } from "../core/facts/errors.js";
3
3
  import { contractState } from "../core/facts/observation.js";
4
4
  import { decidePlacement } from "../core/verbs/placement.js";
5
+ import { observeGitForAdmissionAt } from "../git/observe.js";
5
6
  import { reconcileEffectFailure } from "../git/reconcile.js";
6
7
  import { GitPlumbingError } from "../git/repository.js";
7
- import { acquireTargetPlacementFence, followTargetPlacement, prepareTargetPlacement, observeTargetHead, } from "../git/target-placement.js";
8
+ import { acquireTargetPlacementFence, followTargetPlacement, prepareTargetPlacement, observeTargetHead, observeTargetPlacement, } from "../git/target-placement.js";
8
9
  import { admitDecidedOffer, mintAttempts } from "./attempt.js";
9
10
  import { prepareProtocolAttempt, runProtocol, } from "./run.js";
10
11
  function placementFailure(error) {
11
12
  return { kind: "placement-failed", diagnostic: error instanceof Error ? error.message : String(error) };
12
13
  }
13
- function expectedPlacementFailure(error) {
14
- if (error instanceof GitPlumbingError || error instanceof SqliteTransactionLockError)
15
- return placementFailure(error);
14
+ function expectedOperationalDiagnostic(error) {
15
+ if (error instanceof GitPlumbingError || error instanceof SqliteTransactionLockError) {
16
+ return error instanceof Error ? error.message : String(error);
17
+ }
16
18
  throw error;
17
19
  }
20
+ function expectedPlacementFailure(error) {
21
+ return { kind: "placement-failed", diagnostic: expectedOperationalDiagnostic(error) };
22
+ }
23
+ /** Observe prospective target followability without fencing or placing. */
24
+ export async function observeProspectiveTargetPlacement(repository, input) {
25
+ try {
26
+ const observed = await observeTargetPlacement(repository, input);
27
+ return observed.kind === "ready"
28
+ ? { kind: "ready" }
29
+ : { kind: "refused", refusal: observed.refusal };
30
+ }
31
+ catch (error) {
32
+ return { kind: "failed", diagnostic: expectedOperationalDiagnostic(error) };
33
+ }
34
+ }
18
35
  async function runFencedPlacement(repository, input, protocol) {
19
36
  for (let index = 0; index < protocol.attempts.length; index += 1) {
20
37
  const prepared = await prepareProtocolAttempt(protocol, protocol.attempts[index]);
@@ -25,7 +42,7 @@ async function runFencedPlacement(repository, input, protocol) {
25
42
  throw new Error("placement offer has no contract state");
26
43
  if (prepared.offer.target === undefined)
27
44
  throw new Error("targeted placement offer is missing its target movement");
28
- const observed = observeTargetHead(repository, prepared.offer.target.target);
45
+ const observed = await observeTargetHead(repository, prepared.offer.target.target);
29
46
  if (observed !== prepared.offer.target.expectedOid) {
30
47
  return {
31
48
  kind: "target-moved",
@@ -35,7 +52,7 @@ async function runFencedPlacement(repository, input, protocol) {
35
52
  observed,
36
53
  };
37
54
  }
38
- const physical = prepareTargetPlacement(repository, state, prepared.offer.target);
55
+ const physical = await prepareTargetPlacement(repository, state, prepared.offer.target);
39
56
  if (physical.kind === "refused")
40
57
  return physical;
41
58
  const result = await admitDecidedOffer({
@@ -47,7 +64,7 @@ async function runFencedPlacement(repository, input, protocol) {
47
64
  primaryContract: input.contractId,
48
65
  });
49
66
  if (result.kind === "accepted") {
50
- return { ...result, physical: followTargetPlacement(repository, physical.placement) };
67
+ return { ...result, physical: await followTargetPlacement(repository, physical.placement) };
51
68
  }
52
69
  if (result.kind === "publication-failed")
53
70
  return result;
@@ -56,20 +73,8 @@ async function runFencedPlacement(repository, input, protocol) {
56
73
  }
57
74
  return { kind: "exhausted" };
58
75
  }
59
- /** Run the sole placement adjudicator inside the target's physical fence. */
60
- export async function admitPlacement(channel, repository, target, input) {
61
- const attempts = mintAttempts({ entryCount: 1 });
62
- const protocol = {
63
- input,
64
- channel,
65
- repository,
66
- contracts: [input.contractId],
67
- attempts,
68
- decide: decidePlacement,
69
- };
70
- const run = () => runProtocol(protocol);
71
- if (target === undefined)
72
- return await run();
76
+ /** Hold the existing target-placement fence for one owner callback. */
77
+ async function runUnderTargetPlacementFence(repository, target, run, onReleaseFailure) {
73
78
  let held;
74
79
  try {
75
80
  held = await acquireTargetPlacementFence(repository, target);
@@ -77,10 +82,12 @@ export async function admitPlacement(channel, repository, target, input) {
77
82
  catch (error) {
78
83
  return placementFailure(error);
79
84
  }
85
+ let produced;
80
86
  let result;
81
87
  let exceptional;
82
88
  try {
83
- result = await runFencedPlacement(repository, input, protocol);
89
+ produced = await run();
90
+ result = produced;
84
91
  }
85
92
  catch (error) {
86
93
  if (error instanceof AuthorityCorruptionError || error instanceof TypeError)
@@ -104,11 +111,37 @@ export async function admitPlacement(channel, repository, target, input) {
104
111
  if (exceptional !== undefined)
105
112
  throw exceptional;
106
113
  if (result === undefined)
107
- throw new Error("placement produced no result");
114
+ throw new Error("fenced target work produced no result");
108
115
  if (releaseFailure !== undefined) {
109
- return result.kind === "accepted"
110
- ? { ...result, physical: reconcileEffectFailure(releaseFailure, result.physical) }
116
+ return produced !== undefined && onReleaseFailure !== undefined
117
+ ? onReleaseFailure(produced, releaseFailure)
111
118
  : placementFailure(releaseFailure);
112
119
  }
113
120
  return result;
114
121
  }
122
+ function isDeliveryMissing(result) {
123
+ return result.kind === "refused" && result.refusal.kind === "delivery-missing";
124
+ }
125
+ /** Run the sole placement adjudicator inside the target's physical fence. */
126
+ export async function admitPlacement(channel, repository, target, input, onDeliveryMissing) {
127
+ const attempts = mintAttempts({ entryCount: 1 });
128
+ const protocol = {
129
+ input,
130
+ channel,
131
+ repository,
132
+ contracts: [input.contractId],
133
+ attempts,
134
+ decide: decidePlacement,
135
+ observe: (observedRepository, observedChannel, contracts) => observeGitForAdmissionAt(observedRepository, observedChannel, contracts),
136
+ };
137
+ if (target === undefined)
138
+ return await runProtocol(protocol);
139
+ return await runUnderTargetPlacementFence(repository, target, async () => {
140
+ const result = await runFencedPlacement(repository, input, protocol);
141
+ if (onDeliveryMissing === undefined || !isDeliveryMissing(result))
142
+ return result;
143
+ return await onDeliveryMissing() ?? result;
144
+ }, (result, error) => result.kind === "accepted"
145
+ ? { ...result, physical: reconcileEffectFailure(error, result.physical) }
146
+ : placementFailure(error));
147
+ }
@@ -1,7 +1,8 @@
1
+ import type { ContractTargetLag, ContractWorkspaceObservation } from "../../git/workspace.js";
1
2
  import type { GitRepository } from "../../git/repository.js";
2
3
  import type { GitDecodeChannel, GitReadObservation } from "../../git/read-observation.js";
3
4
  import { type GateCurrent } from "../../core/facts/gate.js";
4
- import type { ContractId, DeliverData, SnapshotId } from "../../core/facts/types.js";
5
+ import type { ContractId, ContractState, DeliverData, SnapshotId } from "../../core/facts/types.js";
5
6
  export type ContractPhase = "waiting" | "bound" | "pending-delivery" | "claimed" | "abandoned";
6
7
  export type ContractDisposition = "active" | "terminal";
7
8
  export type ContractGateCurrent = GateCurrent;
@@ -9,13 +10,18 @@ export type ContractGateReport = Readonly<{
9
10
  gate: string;
10
11
  current: ContractGateCurrent;
11
12
  }>;
13
+ export type { ContractTargetLag, ContractWorkspaceObservation };
12
14
  export type ContractRow = Readonly<{
13
15
  id: ContractId;
16
+ title: string | null;
14
17
  phase: ContractPhase;
18
+ phaseAt: string;
15
19
  disposition: ContractDisposition;
16
20
  workspace: "worktree" | "here";
17
21
  worktreePath: string | null;
22
+ workspaceObservation: ContractWorkspaceObservation;
18
23
  target: string | null;
24
+ targetLag: ContractTargetLag;
19
25
  delivery: DeliverData | null;
20
26
  targetObservation: Readonly<{
21
27
  head: SnapshotId | null;
@@ -38,6 +44,7 @@ export type ContractObservation = Readonly<{
38
44
  kind: "present";
39
45
  row: ContractRow;
40
46
  }>;
47
+ export declare function phaseAtFor(state: Pick<ContractState, "terminal" | "delivery" | "bound">, bindAt: string): string;
41
48
  /** Build the Contract board from one immutable git observation. */
42
49
  export declare function readContractBoard(observation: GitReadObservation): Promise<ContractBoard>;
43
50
  /** Observe one Contract and its target from one fresh ref epoch. */
@@ -1,28 +1,72 @@
1
- import { extendContractsForAdmissionAt, observeActiveContractWorld, observeContractsForAdmissionInObservationAt, observeDeliveryTargetAt, withContractReadObservationAt, } from "../../git/observe.js";
2
- import { deliveryWorktreePath } from "../../git/workspace.js";
1
+ import { observeActiveContractWorld, observeContractsForAdmissionInObservationAt, observeDeliveryTargetAt, withContractReadObservationAt, } from "../../git/observe.js";
2
+ import { decodeContractDocument } from "../../body/decode.js";
3
+ import { observeTargetLag, observeWorkspace, worktreePath, } from "../../git/workspace.js";
4
+ import { appointmentFor, readPlaceRegister, } from "../../workspace-place.js";
3
5
  import { gateReports } from "../../core/facts/gate.js";
4
- import { prerequisiteStatus } from "../../core/facts/observation.js";
5
- function phaseFor(state, eligible) {
6
+ function titleFor(state) {
7
+ try {
8
+ return decodeContractDocument(state.terms.document.bytes).title;
9
+ }
10
+ catch (error) {
11
+ if (error instanceof TypeError)
12
+ return null;
13
+ throw error;
14
+ }
15
+ }
16
+ function phaseFor(state) {
6
17
  if (state.terminal?.kind === "claimed")
7
18
  return "claimed";
8
19
  if (state.terminal?.kind === "abandoned")
9
20
  return "abandoned";
10
21
  if (state.delivery !== null)
11
22
  return "pending-delivery";
12
- if (state.bound !== null || eligible)
23
+ if (state.bound !== null)
13
24
  return "bound";
14
25
  return "waiting";
15
26
  }
16
- function rowFor(repository, state, targetObservation, eligible) {
27
+ export function phaseAtFor(state, bindAt) {
28
+ return state.terminal?.at ?? state.delivery?.at ?? state.bound?.at ?? bindAt;
29
+ }
30
+ async function managedWorkspaceFacts(repository, state, register, targetObservation) {
31
+ const appointed = appointmentFor(register, state.id);
32
+ if (appointed === undefined) {
33
+ return {
34
+ appointed,
35
+ workspaceObservation: { kind: "unappointed" },
36
+ targetLag: state.coordinates.target === undefined ? { kind: "none" } : { kind: "unknown" },
37
+ };
38
+ }
39
+ const path = worktreePath(repository, appointed.place);
40
+ const [workspaceObservation, targetLag] = await Promise.all([
41
+ observeWorkspace(repository, { kind: "worktree", path }, path),
42
+ observeTargetLag(repository, path, targetObservation?.head),
43
+ ]);
44
+ return { appointed, workspaceObservation, targetLag };
45
+ }
46
+ async function hereWorkspaceFacts(repository, targetObservation) {
47
+ const [workspaceObservation, targetLag] = await Promise.all([
48
+ observeWorkspace(repository, { kind: "here" }, repository.effectiveCwd),
49
+ observeTargetLag(repository, repository.effectiveCwd, targetObservation?.head),
50
+ ]);
51
+ return { appointed: undefined, workspaceObservation, targetLag };
52
+ }
53
+ async function rowFor(repository, state, bindAt, targetObservation, register) {
17
54
  const workspace = state.coordinates.workspace;
18
55
  const gates = gateReports(state);
56
+ const { appointed, workspaceObservation, targetLag } = workspace === "worktree"
57
+ ? await managedWorkspaceFacts(repository, state, register, targetObservation)
58
+ : await hereWorkspaceFacts(repository, targetObservation);
19
59
  return {
20
60
  id: state.id,
21
- phase: phaseFor(state, eligible),
61
+ title: titleFor(state),
62
+ phase: phaseFor(state),
63
+ phaseAt: phaseAtFor(state, bindAt),
22
64
  disposition: state.terminal === null ? "active" : "terminal",
23
65
  workspace,
24
- worktreePath: workspace === "worktree" ? deliveryWorktreePath(repository, state.id) : null,
66
+ worktreePath: appointed === undefined ? null : worktreePath(repository, appointed.place),
67
+ workspaceObservation,
25
68
  target: state.coordinates.target ?? null,
69
+ targetLag,
26
70
  delivery: state.delivery?.data ?? null,
27
71
  targetObservation,
28
72
  gates: {
@@ -34,27 +78,30 @@ function rowFor(repository, state, targetObservation, eligible) {
34
78
  /** Build the Contract board from one immutable git observation. */
35
79
  export async function readContractBoard(observation) {
36
80
  const observed = await observeActiveContractWorld(observation);
81
+ const register = await readPlaceRegister(observation.repository);
37
82
  const rows = [];
38
83
  for (const value of observed.contracts.values()) {
39
84
  if (value.state === null)
40
85
  continue;
41
86
  const state = value.state;
42
- rows.push(observeDeliveryTargetAt(observation, state).then((target) => rowFor(observation.repository, state, target, prerequisiteStatus(state.terms.after, observed.eligibility) === "claimed")));
87
+ const bindAt = value.entries[0].at;
88
+ rows.push(observeDeliveryTargetAt(observation, state).then((target) => rowFor(observation.repository, state, bindAt, target, register)));
43
89
  }
44
90
  return { root: observation.repository.primaryWorktree, state: observed.snapshot, rows: await Promise.all(rows) };
45
91
  }
46
92
  /** Observe one Contract and its target from one fresh ref epoch. */
47
93
  export async function readContractObservationAt(repository, channel, id) {
48
94
  return withContractReadObservationAt(repository, channel, id, async (observation) => {
49
- let observed = await observeContractsForAdmissionInObservationAt(observation, [id]);
50
- const state = observed.decision.get(id) ?? null;
51
- if (state !== null)
52
- observed = await extendContractsForAdmissionAt(channel, observed, state.terms.after);
95
+ const observed = await observeContractsForAdmissionInObservationAt(observation, [id]);
96
+ const record = observed.journals.get(id);
97
+ if (record === undefined)
98
+ throw new Error(`missing requested Contract observation: ${id}`);
99
+ const state = record.state;
53
100
  return state === null
54
101
  ? { kind: "missing", id }
55
102
  : {
56
103
  kind: "present",
57
- row: rowFor(observation.repository, state, await observeDeliveryTargetAt(observation, state), prerequisiteStatus(state.terms.after, observed.decision) === "claimed"),
104
+ row: await rowFor(observation.repository, state, record.entries[0].at, await observeDeliveryTargetAt(observation, state), await readPlaceRegister(observation.repository)),
58
105
  };
59
106
  });
60
107
  }
@@ -1,3 +1,4 @@
1
+ import { type StdioProcessExit } from "./stdio.js";
1
2
  export type LineRpcNotification = Readonly<{
2
3
  method: string;
3
4
  params?: Readonly<Record<string, unknown>>;
@@ -5,20 +6,14 @@ export type LineRpcNotification = Readonly<{
5
6
  export type LineRpcServerRequest = LineRpcNotification & Readonly<{
6
7
  id: number | string;
7
8
  }>;
8
- export type LineRpcExit = Readonly<{
9
- code: number | null;
10
- signal: NodeJS.Signals | null;
11
- stderr: string;
12
- }>;
9
+ export type LineRpcExit = StdioProcessExit;
13
10
  export declare class LineRpcProcess {
14
- private readonly child;
11
+ private readonly process;
15
12
  private readonly pending;
16
13
  private readonly notifications;
17
14
  private readonly serverRequests;
18
- private readonly exited;
19
15
  private nextId;
20
16
  private stdout;
21
- private stderr;
22
17
  private closed;
23
18
  constructor(input: Readonly<{
24
19
  argv: readonly [string, ...string[]];
@@ -1,12 +1,4 @@
1
- import { spawn } from "node:child_process";
2
- import { terminateProcessTree } from "./run.js";
3
- const DEFAULT_DRAIN_TIMEOUT_MS = 1_000;
4
- function bounded(promise, milliseconds) {
5
- return new Promise((resolve, reject) => {
6
- const timer = setTimeout(() => resolve(undefined), milliseconds);
7
- void promise.then((value) => { clearTimeout(timer); resolve(value); }, (error) => { clearTimeout(timer); reject(error); });
8
- });
9
- }
1
+ import { spawnStdioProcess } from "./stdio.js";
10
2
  function object(value) {
11
3
  return value !== null && typeof value === "object" && !Array.isArray(value)
12
4
  ? value
@@ -16,37 +8,25 @@ function text(value) {
16
8
  return typeof value === "string" && value.trim().length > 0 ? value : undefined;
17
9
  }
18
10
  export class LineRpcProcess {
19
- child;
11
+ process;
20
12
  pending = new Map();
21
13
  notifications = new Set();
22
14
  serverRequests = new Set();
23
- exited;
24
15
  nextId = 1;
25
16
  stdout = "";
26
- stderr = "";
27
17
  closed = false;
28
18
  constructor(input) {
29
- this.child = spawn(input.argv[0], input.argv.slice(1), {
30
- cwd: input.cwd,
31
- env: input.env ?? process.env,
32
- detached: true,
33
- stdio: ["pipe", "pipe", "pipe"],
34
- windowsHide: true,
35
- });
36
- this.child.stdout.setEncoding("utf8");
37
- this.child.stderr.setEncoding("utf8");
38
- this.child.stdout.on("data", (chunk) => this.consume(chunk));
39
- this.child.stderr.on("data", (chunk) => { this.stderr = `${this.stderr}${chunk}`.slice(-4_000); });
40
- this.child.on("error", (error) => this.fail(error));
41
- this.exited = new Promise((resolve) => this.child.once("close", (code, signal) => {
19
+ this.process = spawnStdioProcess(input);
20
+ this.process.output.setEncoding("utf8");
21
+ this.process.output.on("data", (chunk) => this.consume(chunk));
22
+ void this.process.exited.then((exit) => {
42
23
  this.closed = true;
43
- this.fail(new Error(`line RPC process exited${code === null ? "" : ` with code ${code}`}`));
44
- resolve({ code, signal, stderr: this.stderr.trim() });
45
- }));
24
+ this.fail(new Error(`line RPC process exited${exit.code === null ? "" : ` with code ${exit.code}`}`));
25
+ });
46
26
  }
47
27
  onNotification(listener) { this.notifications.add(listener); }
48
28
  onServerRequest(listener) { this.serverRequests.add(listener); }
49
- onExit(listener) { void this.exited.then(listener); }
29
+ onExit(listener) { void this.process.exited.then(listener); }
50
30
  consume(chunk) {
51
31
  this.stdout += chunk;
52
32
  for (;;) {
@@ -105,7 +85,7 @@ export class LineRpcProcess {
105
85
  const id = this.nextId++;
106
86
  return new Promise((resolve, reject) => {
107
87
  this.pending.set(id, { resolve, reject });
108
- this.child.stdin.write(`${JSON.stringify({ id, method, ...(params === undefined ? {} : { params }) })}\n`, (error) => {
88
+ this.process.input.write(`${JSON.stringify({ id, method, ...(params === undefined ? {} : { params }) })}\n`, (error) => {
109
89
  if (error === null || error === undefined)
110
90
  return;
111
91
  this.pending.delete(id);
@@ -115,31 +95,24 @@ export class LineRpcProcess {
115
95
  }
116
96
  notify(method) {
117
97
  if (!this.closed)
118
- this.child.stdin.write(`${JSON.stringify({ method })}\n`);
98
+ this.process.input.write(`${JSON.stringify({ method })}\n`);
119
99
  }
120
- async endInputAndDrain(timeoutMs = DEFAULT_DRAIN_TIMEOUT_MS) {
100
+ async endInputAndDrain(timeoutMs) {
121
101
  if (this.closed) {
122
- await this.exited;
102
+ await this.process.exited;
123
103
  return;
124
104
  }
125
105
  this.closed = true;
126
106
  this.fail(new Error("line RPC process is closed"));
127
- this.child.stdin.end();
128
- const drained = await bounded(this.exited, timeoutMs);
129
- if (drained === undefined) {
130
- await terminateProcessTree(this.child.pid, true);
131
- await this.exited;
132
- }
107
+ await this.process.endInputAndDrain(timeoutMs);
133
108
  }
134
109
  async close(force = false) {
135
110
  if (this.closed) {
136
- await this.exited;
111
+ await this.process.exited;
137
112
  return;
138
113
  }
139
114
  this.closed = true;
140
115
  this.fail(new Error("line RPC process is closed"));
141
- this.child.stdin.end();
142
- await terminateProcessTree(this.child.pid, force);
143
- await this.exited;
116
+ await this.process.close(force);
144
117
  }
145
118
  }
@@ -1,3 +1,4 @@
1
+ import { type ChildProcess } from "node:child_process";
1
2
  type ProcessLaunch = Readonly<{
2
3
  readonly argv: readonly string[];
3
4
  readonly cwd?: string;
@@ -28,43 +29,28 @@ type ProcessCancelled = Readonly<{
28
29
  readonly kind: "cancelled";
29
30
  }>;
30
31
  export type ProcessOutcome = ProcessTerminal | ProcessTimeout | ProcessSpawnError | ProcessUnknownExit | ProcessCancelled;
31
- export type ProcessIdentity = Readonly<{
32
- pid: number;
33
- spawnedAt: string;
34
- }>;
35
- export type ProcessIdentityProbe = Readonly<{
36
- kind: "gone" | "alive" | "replaced";
37
- }> | Readonly<{
38
- kind: "unverifiable";
39
- diagnostic: string;
40
- }>;
41
- export type ProcessCollar = Readonly<{
42
- pid: number;
43
- processGroup: number;
44
- spawnedAt: string;
32
+ type ProcessStreamError = Readonly<{
33
+ readonly kind: "stream-error";
34
+ readonly diagnostic: string;
45
35
  }>;
46
- export type ProcessTreeProbe = Readonly<{
47
- kind: "gone";
48
- }> | Readonly<{
49
- kind: "alive";
50
- }> | Readonly<{
51
- kind: "unverifiable";
52
- diagnostic: string;
36
+ export type ProcessConsumption = Readonly<{
37
+ readonly outcome: ProcessOutcome | ProcessStreamError;
38
+ readonly pid: number | null;
53
39
  }>;
54
- export type PutDownEvidence = "killed" | "already-dead" | "alive-after-sigkill" | "unavailable";
55
40
  export type DetachedProcessInput = Readonly<{
56
41
  argv: readonly string[];
57
42
  cwd: string;
58
43
  env?: NodeJS.ProcessEnv;
59
44
  log: string;
60
45
  }>;
61
- export declare function terminateProcessTree(pid: number | undefined, force?: boolean): Promise<void>;
62
- export declare function currentProcessCollar(): ProcessCollar;
63
- export declare function currentProcessIdentity(): ProcessIdentity;
64
- export declare function probeProcessIdentity(identity: ProcessIdentity): ProcessIdentityProbe;
65
- export declare function spawnDetachedProcess(input: DetachedProcessInput): Promise<ProcessCollar>;
66
- export declare function probeProcessTree(collar: ProcessCollar): ProcessTreeProbe;
67
- export declare function putDownProcessTree(collar: ProcessCollar): Promise<PutDownEvidence>;
46
+ export type OwnedProcess = Readonly<{
47
+ pid: number;
48
+ terminate(force?: boolean): Promise<void>;
49
+ release(): void;
50
+ }>;
51
+ export declare function terminateOwnedProcess(child: ChildProcess, force?: boolean): Promise<void>;
52
+ export declare function spawnDetachedProcess(input: DetachedProcessInput): Promise<OwnedProcess>;
68
53
  export declare function runProcess(input: ProcessInput): Promise<ProcessOutcome>;
69
54
  export declare function runProcessToExit(input: ProcessLaunch): Promise<ProcessOutcome>;
55
+ export declare function consumeProcessStdout(input: ProcessInput, consume: (chunk: Buffer) => void): Promise<ProcessConsumption>;
70
56
  export {};