@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.
Files changed (278) hide show
  1. package/README.md +1 -1
  2. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +3 -0
  3. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +3 -0
  4. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +3 -6
  5. package/build/src/akuma/akuma-errors.d.ts +6 -0
  6. package/build/src/akuma/akuma-errors.js +9 -0
  7. package/build/src/akuma/akuma-handle.d.ts +39 -0
  8. package/build/src/akuma/akuma-handle.js +263 -0
  9. package/build/src/akuma/akuma-observe.d.ts +19 -0
  10. package/build/src/akuma/akuma-observe.js +74 -0
  11. package/build/src/akuma/akuma-product-symbols.d.ts +1 -0
  12. package/build/src/akuma/akuma-product-symbols.js +1 -0
  13. package/build/src/akuma/akuma-product.d.ts +18 -0
  14. package/build/src/akuma/akuma-product.js +154 -0
  15. package/build/src/akuma/akuma.d.ts +37 -81
  16. package/build/src/akuma/akuma.js +39 -447
  17. package/build/src/akuma/allowed.d.ts +1 -0
  18. package/build/src/akuma/allowed.js +6 -0
  19. package/build/src/akuma/archetype.js +8 -5
  20. package/build/src/akuma/body-supervisor.d.ts +26 -0
  21. package/build/src/akuma/body-supervisor.js +89 -0
  22. package/build/src/akuma/body.d.ts +45 -4
  23. package/build/src/akuma/body.js +145 -35
  24. package/build/src/akuma/coordinate.js +17 -7
  25. package/build/src/akuma/heart/facts.js +1 -1
  26. package/build/src/akuma/heart/index.d.ts +1 -1
  27. package/build/src/akuma/heart/index.js +2 -4
  28. package/build/src/akuma/heart/request-authority.js +4 -4
  29. package/build/src/akuma/heart/request-rows.js +27 -16
  30. package/build/src/akuma/heart/rows.js +79 -41
  31. package/build/src/akuma/heart/soul.js +1 -1
  32. package/build/src/akuma/heart/storage.d.ts +1 -0
  33. package/build/src/akuma/heart/storage.js +76 -11
  34. package/build/src/akuma/heart/tells.js +35 -18
  35. package/build/src/akuma/heart/timeline.js +19 -11
  36. package/build/src/akuma/identity.d.ts +1 -0
  37. package/build/src/akuma/identity.js +1 -0
  38. package/build/src/akuma/index.d.ts +3 -3
  39. package/build/src/akuma/index.js +1 -1
  40. package/build/src/akuma/nuke.js +106 -14
  41. package/build/src/akuma/projection-read.d.ts +17 -0
  42. package/build/src/akuma/projection-read.js +135 -0
  43. package/build/src/akuma/projection.d.ts +1 -16
  44. package/build/src/akuma/projection.js +77 -154
  45. package/build/src/akuma/provider-recipe.js +11 -5
  46. package/build/src/akuma/provider.js +105 -62
  47. package/build/src/akuma/providers/acp/config.js +5 -6
  48. package/build/src/akuma/providers/acp/core.js +17 -10
  49. package/build/src/akuma/providers/acp/events.js +38 -29
  50. package/build/src/akuma/providers/acp/index.js +13 -9
  51. package/build/src/akuma/providers/claude/events.js +8 -12
  52. package/build/src/akuma/providers/claude/index.d.ts +1 -0
  53. package/build/src/akuma/providers/claude/index.js +55 -26
  54. package/build/src/akuma/providers/claude/input.js +6 -2
  55. package/build/src/akuma/providers/codex-app-server/events.js +61 -36
  56. package/build/src/akuma/providers/codex-app-server/index.js +25 -13
  57. package/build/src/akuma/providers/grok-build/index.js +22 -25
  58. package/build/src/akuma/providers/index.js +2 -18
  59. package/build/src/akuma/providers/opencode-sdk/events.js +32 -18
  60. package/build/src/akuma/providers/opencode-sdk/index.js +26 -17
  61. package/build/src/akuma/providers/opencode-sdk/session.js +21 -5
  62. package/build/src/akuma/providers/pi/events.js +26 -19
  63. package/build/src/akuma/providers/pi/index.js +58 -40
  64. package/build/src/akuma/publication.js +9 -9
  65. package/build/src/akuma/request-execution.d.ts +6 -0
  66. package/build/src/akuma/request-execution.js +86 -0
  67. package/build/src/akuma/request-serve.d.ts +12 -1
  68. package/build/src/akuma/request-serve.js +19 -122
  69. package/build/src/akuma/request-wire.d.ts +2 -2
  70. package/build/src/akuma/request-wire.js +75 -102
  71. package/build/src/akuma/requests.js +2 -2
  72. package/build/src/akuma/{body-turn.d.ts → turn-drive.d.ts} +2 -25
  73. package/build/src/akuma/{body-turn.js → turn-drive.js} +20 -103
  74. package/build/src/akuma-body.js +6 -5
  75. package/build/src/body/amend.d.ts +4 -1
  76. package/build/src/body/amend.js +14 -6
  77. package/build/src/body/decode.js +2 -4
  78. package/build/src/body/envelope.js +2 -4
  79. package/build/src/body/region.d.ts +1 -2
  80. package/build/src/body/region.js +2 -70
  81. package/build/src/body/render.js +17 -12
  82. package/build/src/body/shape.js +1 -7
  83. package/build/src/cli/accepted.d.ts +1 -1
  84. package/build/src/cli/accepted.js +8 -1
  85. package/build/src/cli/commands/akuma-invoke.js +18 -6
  86. package/build/src/cli/commands/akuma.d.ts +0 -6
  87. package/build/src/cli/commands/akuma.js +11 -24
  88. package/build/src/cli/commands/amend.d.ts +2 -2
  89. package/build/src/cli/commands/amend.js +2 -4
  90. package/build/src/cli/commands/bind.d.ts +4 -4
  91. package/build/src/cli/commands/bind.js +16 -6
  92. package/build/src/cli/commands/contract-invoke.d.ts +25 -0
  93. package/build/src/cli/commands/contract-invoke.js +193 -0
  94. package/build/src/cli/commands/contract.d.ts +109 -10
  95. package/build/src/cli/commands/contract.js +339 -16
  96. package/build/src/cli/commands/install.d.ts +1 -1
  97. package/build/src/cli/commands/install.js +23 -15
  98. package/build/src/cli/commands/task-invoke.js +187 -67
  99. package/build/src/cli/commands/task-query.js +48 -24
  100. package/build/src/cli/commands/task.js +150 -38
  101. package/build/src/cli/coordinates.js +2 -4
  102. package/build/src/cli/invoke.js +92 -208
  103. package/build/src/cli/main.js +4 -2
  104. package/build/src/cli/parse.d.ts +3 -90
  105. package/build/src/cli/parse.js +27 -233
  106. package/build/src/cli/render/akuma-activity.d.ts +38 -0
  107. package/build/src/cli/render/akuma-activity.js +362 -0
  108. package/build/src/cli/render/akuma-tool-command.js +6 -7
  109. package/build/src/cli/render/akuma-tool.js +10 -8
  110. package/build/src/cli/render/akuma.d.ts +1 -1
  111. package/build/src/cli/render/akuma.js +61 -353
  112. package/build/src/cli/render/audit.js +5 -15
  113. package/build/src/cli/render/board.d.ts +1 -0
  114. package/build/src/cli/render/board.js +17 -0
  115. package/build/src/cli/render/catalog.js +81 -8
  116. package/build/src/cli/render/contract-history.d.ts +2 -0
  117. package/build/src/cli/render/contract-history.js +83 -0
  118. package/build/src/cli/render/contract-observation.d.ts +10 -0
  119. package/build/src/cli/render/contract-observation.js +58 -0
  120. package/build/src/cli/render/contract.d.ts +1 -2
  121. package/build/src/cli/render/contract.js +41 -142
  122. package/build/src/cli/render/kanshi.js +239 -215
  123. package/build/src/cli/render/receipt.js +2 -9
  124. package/build/src/cli/render/refusal.js +10 -15
  125. package/build/src/cli/render/region.js +22 -6
  126. package/build/src/cli/render/settings.js +4 -4
  127. package/build/src/cli/render/task.js +34 -14
  128. package/build/src/cli/render/terminal.js +16 -5
  129. package/build/src/cli/result.d.ts +3 -3
  130. package/build/src/cli/runtime.d.ts +6 -1
  131. package/build/src/cli/runtime.js +84 -63
  132. package/build/src/cli/selectors.js +2 -4
  133. package/build/src/cli/usage.js +6 -5
  134. package/build/src/contract-worktree.d.ts +1 -23
  135. package/build/src/contract-worktree.js +79 -155
  136. package/build/src/coordination/durable-file.js +7 -3
  137. package/build/src/coordination/sqlite-transaction-lock.js +29 -12
  138. package/build/src/core/facts/codec.js +7 -3
  139. package/build/src/core/facts/types.d.ts +1 -1
  140. package/build/src/core/subject.js +16 -9
  141. package/build/src/core/verbs/abandon.d.ts +1 -1
  142. package/build/src/core/verbs/abandon.js +1 -1
  143. package/build/src/core/verbs/amend.d.ts +1 -1
  144. package/build/src/core/verbs/amend.js +7 -7
  145. package/build/src/core/verbs/attestation.js +4 -1
  146. package/build/src/core/verbs/bind.d.ts +1 -1
  147. package/build/src/core/verbs/bind.js +1 -1
  148. package/build/src/core/verbs/placement.d.ts +1 -1
  149. package/build/src/core/verbs/placement.js +10 -8
  150. package/build/src/dispatch/index.js +8 -6
  151. package/build/src/duration.js +5 -1
  152. package/build/src/git/admission.js +5 -3
  153. package/build/src/git/hooks.d.ts +8 -0
  154. package/build/src/git/hooks.js +63 -14
  155. package/build/src/git/identity.js +1 -4
  156. package/build/src/git/integration.d.ts +2 -3
  157. package/build/src/git/integration.js +32 -39
  158. package/build/src/git/nuke.d.ts +1 -1
  159. package/build/src/git/nuke.js +110 -57
  160. package/build/src/git/observe.d.ts +13 -1
  161. package/build/src/git/observe.js +51 -44
  162. package/build/src/git/process.js +4 -2
  163. package/build/src/git/read-observation.js +10 -8
  164. package/build/src/git/reconcile.d.ts +22 -2
  165. package/build/src/git/reconcile.js +38 -274
  166. package/build/src/git/repository.d.ts +2 -0
  167. package/build/src/git/repository.js +13 -22
  168. package/build/src/git/scratch.d.ts +8 -5
  169. package/build/src/git/scratch.js +29 -2
  170. package/build/src/git/target-placement.d.ts +9 -10
  171. package/build/src/git/target-placement.js +59 -62
  172. package/build/src/git/tender.js +13 -46
  173. package/build/src/git/terminal-reconcile.d.ts +21 -0
  174. package/build/src/git/terminal-reconcile.js +278 -0
  175. package/build/src/git/terminal-seal.js +15 -12
  176. package/build/src/git/tree.js +5 -2
  177. package/build/src/git/workspace.d.ts +24 -2
  178. package/build/src/git/workspace.js +120 -16
  179. package/build/src/identity/coordinates.js +3 -1
  180. package/build/src/identity/selector.js +6 -3
  181. package/build/src/kanshi/fleet.d.ts +6 -0
  182. package/build/src/kanshi/fleet.js +27 -0
  183. package/build/src/kanshi/index.d.ts +1 -1
  184. package/build/src/kanshi/read.js +60 -48
  185. package/build/src/kanshi/report.d.ts +11 -23
  186. package/build/src/kanshi/select.js +25 -24
  187. package/build/src/library/address.d.ts +2 -1
  188. package/build/src/library/address.js +7 -8
  189. package/build/src/library/akuma-creation.js +20 -12
  190. package/build/src/library/audit.d.ts +0 -1
  191. package/build/src/library/audit.js +1 -2
  192. package/build/src/library/bind.d.ts +53 -5
  193. package/build/src/library/bind.js +98 -22
  194. package/build/src/library/catalog.d.ts +1 -0
  195. package/build/src/library/catalog.js +6 -3
  196. package/build/src/library/configuration.js +3 -1
  197. package/build/src/library/continuation.d.ts +15 -4
  198. package/build/src/library/continuation.js +24 -16
  199. package/build/src/library/contract-bind.d.ts +26 -0
  200. package/build/src/library/contract-bind.js +106 -0
  201. package/build/src/library/contract-operations.d.ts +77 -0
  202. package/build/src/library/contract-operations.js +130 -0
  203. package/build/src/library/contract-types.d.ts +95 -0
  204. package/build/src/library/contract-types.js +1 -0
  205. package/build/src/library/contract.d.ts +19 -154
  206. package/build/src/library/contract.js +46 -261
  207. package/build/src/library/fleet.d.ts +1 -1
  208. package/build/src/library/fleet.js +19 -11
  209. package/build/src/library/input.d.ts +2 -0
  210. package/build/src/library/input.js +14 -0
  211. package/build/src/library/keiyaku.d.ts +8 -7
  212. package/build/src/library/keiyaku.js +3 -3
  213. package/build/src/library/nuke.js +14 -5
  214. package/build/src/library/reconcile.d.ts +9 -4
  215. package/build/src/library/reconcile.js +30 -24
  216. package/build/src/library/refusal.d.ts +13 -5
  217. package/build/src/library/refusal.js +7 -0
  218. package/build/src/library/region.d.ts +8 -7
  219. package/build/src/library/region.js +31 -11
  220. package/build/src/library/repo.js +2 -2
  221. package/build/src/markdown/lex.js +9 -2
  222. package/build/src/markdown/parse.js +11 -7
  223. package/build/src/markdown/query.js +4 -2
  224. package/build/src/protocol/amend.js +3 -1
  225. package/build/src/protocol/attempt.d.ts +7 -3
  226. package/build/src/protocol/attempt.js +9 -5
  227. package/build/src/protocol/audit.d.ts +1 -1
  228. package/build/src/protocol/audit.js +4 -14
  229. package/build/src/protocol/bind.d.ts +19 -6
  230. package/build/src/protocol/bind.js +28 -13
  231. package/build/src/protocol/completion.d.ts +0 -1
  232. package/build/src/protocol/completion.js +1 -3
  233. package/build/src/protocol/deliver.d.ts +1 -3
  234. package/build/src/protocol/deliver.js +15 -39
  235. package/build/src/protocol/intent.d.ts +1 -0
  236. package/build/src/protocol/intent.js +9 -7
  237. package/build/src/protocol/operations.d.ts +3 -7
  238. package/build/src/protocol/operations.js +5 -3
  239. package/build/src/protocol/placement.d.ts +0 -1
  240. package/build/src/protocol/placement.js +6 -6
  241. package/build/src/protocol/read/observation.d.ts +11 -0
  242. package/build/src/protocol/read/observation.js +37 -0
  243. package/build/src/protocol/read/status.d.ts +23 -12
  244. package/build/src/protocol/read/status.js +90 -72
  245. package/build/src/protocol/reconcile.d.ts +1 -0
  246. package/build/src/protocol/reconcile.js +2 -3
  247. package/build/src/protocol/reintegrate.js +1 -1
  248. package/build/src/protocol/review.d.ts +0 -1
  249. package/build/src/protocol/review.js +15 -22
  250. package/build/src/protocol/run.d.ts +1 -0
  251. package/build/src/protocol/run.js +4 -5
  252. package/build/src/runtime/proc/launch.d.ts +33 -0
  253. package/build/src/runtime/proc/launch.js +98 -0
  254. package/build/src/runtime/proc/line-rpc.js +9 -3
  255. package/build/src/runtime/proc/run.d.ts +12 -6
  256. package/build/src/runtime/proc/run.js +78 -21
  257. package/build/src/runtime/proc/stdio.js +7 -10
  258. package/build/src/settings.js +6 -2
  259. package/build/src/settlement/holder.js +13 -10
  260. package/build/src/settlement/settle.js +39 -6
  261. package/build/src/task/board.js +12 -6
  262. package/build/src/task/compose.js +85 -22
  263. package/build/src/task/context.js +21 -8
  264. package/build/src/task/document.js +46 -10
  265. package/build/src/task/identity.js +5 -2
  266. package/build/src/task/index.d.ts +0 -1
  267. package/build/src/task/index.js +119 -34
  268. package/build/src/task/input.js +29 -7
  269. package/build/src/task/mutation.js +61 -19
  270. package/build/src/task/operations.d.ts +6 -1
  271. package/build/src/task/operations.js +83 -29
  272. package/build/src/task/query.js +55 -26
  273. package/build/src/task/store.d.ts +3 -1
  274. package/build/src/task/store.js +81 -17
  275. package/build/src/verification/execution.js +14 -11
  276. package/build/src/workspace-place.js +19 -7
  277. package/build/src/world.js +16 -6
  278. package/package.json +8 -3
@@ -6,15 +6,21 @@ import { parseTreeObject, prepareTreeUpdate, treeUpdate, validPath, } from "./tr
6
6
  export const GIT_REF = "refs/heads/keiyaku-state";
7
7
  export const DELIVERY_REF_NAMESPACE = "refs/heads/keiyaku-delivery";
8
8
  export const CANDIDATE_PIN_REF_NAMESPACE = "refs/heads/keiyaku-candidate";
9
+ export const MIGRATION_DELIVERY_REF_NAMESPACE = "refs/keiyaku/delivery";
10
+ export const MIGRATION_CANDIDATE_PIN_REF_NAMESPACE = "refs/keiyaku/candidate";
9
11
  export const GIT_FORMAT_PATH = "meta/format.json";
10
12
  const CURRENT_FORMAT_VERSION = 4;
11
13
  export const GIT_FORMAT_BYTES = `{"version":${CURRENT_FORMAT_VERSION}}\n`;
12
14
  export function isKeiyakuOwnedRef(ref) {
13
- return ref === GIT_REF
14
- || ref === DELIVERY_REF_NAMESPACE
15
- || ref.startsWith(`${DELIVERY_REF_NAMESPACE}/`)
16
- || ref === CANDIDATE_PIN_REF_NAMESPACE
17
- || ref.startsWith(`${CANDIDATE_PIN_REF_NAMESPACE}/`);
15
+ return (ref === GIT_REF ||
16
+ ref === DELIVERY_REF_NAMESPACE ||
17
+ ref.startsWith(`${DELIVERY_REF_NAMESPACE}/`) ||
18
+ ref === CANDIDATE_PIN_REF_NAMESPACE ||
19
+ ref.startsWith(`${CANDIDATE_PIN_REF_NAMESPACE}/`) ||
20
+ ref === MIGRATION_DELIVERY_REF_NAMESPACE ||
21
+ ref.startsWith(`${MIGRATION_DELIVERY_REF_NAMESPACE}/`) ||
22
+ ref === MIGRATION_CANDIDATE_PIN_REF_NAMESPACE ||
23
+ ref.startsWith(`${MIGRATION_CANDIDATE_PIN_REF_NAMESPACE}/`));
18
24
  }
19
25
  export class NoGitWorldError extends Error {
20
26
  path;
@@ -181,15 +187,7 @@ export async function readGitPaths(repository, requestedPaths) {
181
187
  if (commit === null)
182
188
  return { commit: null, tree: null, paths: new Map() };
183
189
  const tree = await readTreeForCommit(repository, commit);
184
- const paths = parseTreeEntries(await runGit(repository, [
185
- "ls-tree",
186
- "-z",
187
- "--full-tree",
188
- tree,
189
- "--",
190
- GIT_FORMAT_PATH,
191
- ...requestedPaths,
192
- ]));
190
+ const paths = parseTreeEntries(await runGit(repository, ["ls-tree", "-z", "--full-tree", tree, "--", GIT_FORMAT_PATH, ...requestedPaths]));
193
191
  await validateGitFormat(repository, paths);
194
192
  return { commit, tree, paths };
195
193
  }
@@ -394,14 +392,7 @@ export async function updateRefsAtomically(repository, updates, assertions = [])
394
392
  if (git === undefined || git.ref !== GIT_REF) {
395
393
  throw new Error(`the first atomic update must be the Git ref: ${GIT_REF}`);
396
394
  }
397
- const lines = [
398
- "start",
399
- ...assertions.map(refAssertionLine),
400
- ...updates.map(refUpdateLine),
401
- "prepare",
402
- "commit",
403
- "",
404
- ];
395
+ const lines = ["start", ...assertions.map(refAssertionLine), ...updates.map(refUpdateLine), "prepare", "commit", ""];
405
396
  try {
406
397
  await runGit(repository, ["update-ref", "--stdin"], lines.join("\n"));
407
398
  return { kind: "published" };
@@ -4,14 +4,17 @@ export type MaterializedScratchCandidate = Readonly<{
4
4
  cwd: string;
5
5
  dispose: () => Promise<WorktreeLeak | null>;
6
6
  }>;
7
- export type CollectableScratchWorktree = Readonly<{
8
- path: string;
9
- release(): void;
10
- }>;
11
7
  export type WorktreeLeak = Readonly<{
12
8
  path: string;
13
9
  diagnostic: string;
14
10
  }>;
15
- export declare function collectableScratchWorktrees(paths: Iterable<string>): Promise<readonly CollectableScratchWorktree[]>;
11
+ type CollectableScratchRemoval = Readonly<{
12
+ path: string;
13
+ action: "removed" | "unchanged";
14
+ retained: boolean;
15
+ }>;
16
+ /** Remove only abandoned Verification scratch worktrees whose ownership locks are held. */
17
+ export declare function removeCollectableScratchWorktrees(repository: GitRepository, paths: Set<string>): Promise<readonly CollectableScratchRemoval[]>;
16
18
  /** Materialize a disposable candidate worktree and own only its removal. */
17
19
  export declare function materializeScratchCandidate(repository: GitRepository, candidate: SnapshotId): Promise<MaterializedScratchCandidate>;
20
+ export {};
@@ -1,5 +1,5 @@
1
1
  import { randomBytes } from "node:crypto";
2
- import { realpath } from "node:fs/promises";
2
+ import { access, realpath } from "node:fs/promises";
3
3
  import { tmpdir } from "node:os";
4
4
  import { basename, dirname, join } from "node:path";
5
5
  import { tryAcquireSqliteTransactionLock, } from "../coordination/sqlite-transaction-lock.js";
@@ -17,7 +17,7 @@ function ownershipLockPath(path) {
17
17
  const match = SCRATCH_PATTERN.exec(basename(path));
18
18
  return match === null ? null : join(SCRATCH_ROOT, `.${SCRATCH_PREFIX}${match[1]}.owner.sqlite`);
19
19
  }
20
- export async function collectableScratchWorktrees(paths) {
20
+ async function collectableScratchWorktrees(paths) {
21
21
  const collectable = [];
22
22
  for (const path of paths) {
23
23
  const lockPath = ownershipLockPath(path);
@@ -29,6 +29,33 @@ export async function collectableScratchWorktrees(paths) {
29
29
  }
30
30
  return collectable;
31
31
  }
32
+ const pathExists = (path) => access(path).then(() => true, () => false);
33
+ /** Remove only abandoned Verification scratch worktrees whose ownership locks are held. */
34
+ export async function removeCollectableScratchWorktrees(repository, paths) {
35
+ const removals = [];
36
+ for (const scratch of await collectableScratchWorktrees(paths)) {
37
+ try {
38
+ if (!(await pathExists(scratch.path))) {
39
+ await runGit(repository, ["worktree", "remove", scratch.path]);
40
+ paths.delete(scratch.path);
41
+ removals.push({ path: scratch.path, action: "removed", retained: false });
42
+ continue;
43
+ }
44
+ try {
45
+ await runGit(repository, ["worktree", "remove", "--force", scratch.path]);
46
+ paths.delete(scratch.path);
47
+ removals.push({ path: scratch.path, action: "removed", retained: false });
48
+ }
49
+ catch {
50
+ removals.push({ path: scratch.path, action: "unchanged", retained: true });
51
+ }
52
+ }
53
+ finally {
54
+ scratch.release();
55
+ }
56
+ }
57
+ return removals;
58
+ }
32
59
  /** Materialize a disposable candidate worktree and own only its removal. */
33
60
  export async function materializeScratchCandidate(repository, candidate) {
34
61
  const cwd = scratchPath();
@@ -2,12 +2,6 @@ import { type HeldSqliteTransactionLock } from "../coordination/sqlite-transacti
2
2
  import type { ContractId, ContractState, SnapshotId } from "../core/facts/types.js";
3
3
  import type { RefOperation } from "../core/facts/offer.js";
4
4
  import { type GitRepository } from "./process.js";
5
- export type WorkspaceNotOnTargetRefusal = Readonly<{
6
- kind: "workspace-not-on-target";
7
- contractId: ContractId;
8
- target: string;
9
- branch: string | null;
10
- }>;
11
5
  export type CheckoutNotFollowableRefusal = Readonly<{
12
6
  kind: "checkout-not-followable";
13
7
  contractId: ContractId;
@@ -16,7 +10,7 @@ export type CheckoutNotFollowableRefusal = Readonly<{
16
10
  reason: "staged" | "conflict" | "untracked";
17
11
  paths: readonly string[];
18
12
  }>;
19
- export type TargetPlacementRefusal = CheckoutNotFollowableRefusal | WorkspaceNotOnTargetRefusal;
13
+ export type TargetPlacementRefusal = CheckoutNotFollowableRefusal;
20
14
  export type TargetCheckoutEffect = Readonly<{
21
15
  kind: "target-checkout";
22
16
  path: string;
@@ -34,7 +28,7 @@ export type TargetPlacementPhysicalResult = Readonly<{
34
28
  lag: readonly TargetCheckoutLag[];
35
29
  }>;
36
30
  type FollowArm = Readonly<{
37
- kind: "ordinary" | "here";
31
+ kind: "ordinary";
38
32
  path: string;
39
33
  }>;
40
34
  export type PreparedTargetPlacement = Readonly<{
@@ -58,7 +52,6 @@ export type TargetPlacementObservationInput = Readonly<{
58
52
  coordinates: TargetedContractCoordinates;
59
53
  predecessor: SnapshotId;
60
54
  candidate: SnapshotId;
61
- hereWorkspacePath?: string;
62
55
  }>;
63
56
  export type TargetPlacementObservation = Readonly<{
64
57
  kind: "ready";
@@ -87,7 +80,13 @@ export type AuditTargetAnswer = Readonly<{
87
80
  }>;
88
81
  /** Adjudicate the complete post-Verification audit target answer without placing. */
89
82
  export declare function adjudicateAuditTarget(repository: GitRepository, input: TargetPlacementObservationInput): Promise<AuditTargetAnswer>;
90
- export declare function prepareTargetPlacement(repository: GitRepository, state: ContractState, target: RefOperation, hereWorkspacePath?: string): Promise<TargetPlacementPreparation>;
83
+ export declare function prepareTargetPlacement(repository: GitRepository, state: ContractState, target: RefOperation): Promise<TargetPlacementPreparation>;
91
84
  export declare function followTargetPlacement(repository: GitRepository, prepared: PreparedTargetPlacement): Promise<TargetPlacementPhysicalResult>;
85
+ /** Judge the target-checkout shape without mutating refs, worktrees, or indexes. */
86
+ export declare function observeTargetCheckoutShape(repository: GitRepository, input: Readonly<{
87
+ path: string;
88
+ predecessor: SnapshotId;
89
+ candidate: SnapshotId;
90
+ }>): Promise<"complete" | "recoverable" | "retained">;
92
91
  export declare function recoverTargetPlacement(repository: GitRepository, state: ContractState): Promise<TargetPlacementPhysicalResult>;
93
92
  export {};
@@ -1,10 +1,9 @@
1
1
  import { lstat } from "node:fs/promises";
2
2
  import { resolve } from "node:path";
3
- import { acquireSqliteTransactionLock } from "../coordination/sqlite-transaction-lock.js";
3
+ import { acquireSqliteTransactionLock, } from "../coordination/sqlite-transaction-lock.js";
4
4
  import { gitObjectId, gitObjectIdForSnapshot, gitRefLocator, mintSnapshotId } from "./identity.js";
5
- import { currentBranch } from "./observe.js";
6
- import { commonGitDirectory, readRef, registeredWorktrees, } from "./repository.js";
7
- import { consumeGitStdout, GitPlumbingError, runGit, } from "./process.js";
5
+ import { commonGitDirectory, readRef, registeredWorktrees } from "./repository.js";
6
+ import { consumeGitStdout, GitPlumbingError, runGit } from "./process.js";
8
7
  import { captureWorkspaceTree } from "./workspace.js";
9
8
  export async function observeTargetHead(repository, target) {
10
9
  const value = await readRef(repository, target);
@@ -26,9 +25,7 @@ function literalPath(path) {
26
25
  return `:(literal)${path}`;
27
26
  }
28
27
  async function commitTree(repository, snapshot) {
29
- return gitObjectId((await runGit(repository, ["show", "-s", "--format=%T", gitObjectIdForSnapshot(snapshot)]))
30
- .toString("utf8")
31
- .trim(), "commit tree");
28
+ return gitObjectId((await runGit(repository, ["show", "-s", "--format=%T", gitObjectIdForSnapshot(snapshot)])).toString("utf8").trim(), "commit tree");
32
29
  }
33
30
  function checkoutRefusal(contractId, target, path, reason, paths) {
34
31
  return { kind: "checkout-not-followable", contractId, target: target.target, path, reason, paths };
@@ -64,11 +61,18 @@ async function dryRunRefusal(input, scopes) {
64
61
  const dirty = await gitPaths(repository, path, ["diff-files", "--name-only", "-z", "--", ...pathspecs]);
65
62
  if (dirty.length > 0)
66
63
  return checkoutRefusal(contractId, target, path, "conflict", dirty);
67
- const unmerged = await gitPaths(repository, path, ["diff", "--name-only", "--diff-filter=U", "-z", "--", ...pathspecs]);
64
+ const unmerged = await gitPaths(repository, path, [
65
+ "diff",
66
+ "--name-only",
67
+ "--diff-filter=U",
68
+ "-z",
69
+ "--",
70
+ ...pathspecs,
71
+ ]);
68
72
  if (unmerged.length > 0)
69
73
  return checkoutRefusal(contractId, target, path, "conflict", unmerged);
70
- return await untrackedRefusalWithinScopes(input, scopes, false)
71
- ?? checkoutRefusal(contractId, target, path, "conflict", []);
74
+ return ((await untrackedRefusalWithinScopes(input, scopes, false)) ??
75
+ checkoutRefusal(contractId, target, path, "conflict", []));
72
76
  }
73
77
  async function physicalScope(worktree, candidatePath) {
74
78
  const components = candidatePath.split("/");
@@ -93,7 +97,10 @@ async function physicalScope(worktree, candidatePath) {
93
97
  }
94
98
  async function candidateEntryIsBlob(repository, path, candidate, candidatePath) {
95
99
  const output = await runGit(repository, ["-C", path, "ls-tree", "-z", candidate, "--", literalPath(candidatePath)]);
96
- const records = output.toString("utf8").split("\0").filter((record) => record.length > 0);
100
+ const records = output
101
+ .toString("utf8")
102
+ .split("\0")
103
+ .filter((record) => record.length > 0);
97
104
  if (records.length !== 1)
98
105
  throw new Error(`candidate path has no unique Git entry: ${candidatePath}`);
99
106
  const separator = records[0].indexOf("\t");
@@ -142,13 +149,7 @@ async function untrackedRefusalWithinScopes(input, scopes, ignored) {
142
149
  if (scope.kind !== "directory")
143
150
  continue;
144
151
  let found = false;
145
- await consumeGitStdout(repository, [
146
- "-C",
147
- path,
148
- ...args,
149
- "--",
150
- literalPath(scope.path),
151
- ], (chunk) => {
152
+ await consumeGitStdout(repository, ["-C", path, ...args, "--", literalPath(scope.path)], (chunk) => {
152
153
  if (chunk.length > 0)
153
154
  found = true;
154
155
  });
@@ -158,10 +159,11 @@ async function untrackedRefusalWithinScopes(input, scopes, ignored) {
158
159
  return null;
159
160
  }
160
161
  async function indexMatchesTreeOnPaths(repository, path, tree, paths) {
161
- return (await gitPaths(repository, path, ["diff-index", "--cached", "--name-only", "-z", tree, "--", ...paths])).length === 0;
162
+ return ((await gitPaths(repository, path, ["diff-index", "--cached", "--name-only", "-z", tree, "--", ...paths])).length ===
163
+ 0);
162
164
  }
163
165
  async function workspaceMatchesTreeOnPaths(repository, path, tree, workspaceTree, paths) {
164
- return (await gitPaths(repository, path, ["diff", "--name-only", "-z", tree, workspaceTree, "--", ...paths])).length === 0;
166
+ return ((await gitPaths(repository, path, ["diff", "--name-only", "-z", tree, workspaceTree, "--", ...paths])).length === 0);
165
167
  }
166
168
  async function ordinaryPrecheck(repository, contractId, target, path) {
167
169
  const predecessor = gitObjectIdForSnapshot(target.expectedOid);
@@ -199,35 +201,13 @@ export async function observeTargetPlacement(repository, input) {
199
201
  const worktrees = (await registeredWorktrees(repository))
200
202
  .filter((worktree) => worktree.branch === target.target)
201
203
  .sort((left, right) => left.path.localeCompare(right.path));
202
- const hereSource = input.coordinates.workspace === "here" ? input.hereWorkspacePath ?? null : null;
203
- if (hereSource !== null) {
204
- const branch = await currentBranch(repository, hereSource);
205
- if (branch !== target.target) {
206
- return {
207
- kind: "refused",
208
- refusal: {
209
- kind: "workspace-not-on-target",
210
- contractId: input.contractId,
211
- target: target.target,
212
- branch,
213
- },
214
- };
215
- }
216
- }
217
204
  const arms = [];
218
205
  for (const worktree of worktrees) {
219
- const kind = hereSource !== null && resolve(worktree.path) === hereSource ? "here" : "ordinary";
220
- if (kind === "ordinary") {
221
- const refusal = await ordinaryPrecheck(repository, input.contractId, target, worktree.path);
222
- if (refusal !== null)
223
- return { kind: "refused", refusal };
224
- }
225
- arms.push({ kind, path: worktree.path });
206
+ const refusal = await ordinaryPrecheck(repository, input.contractId, target, worktree.path);
207
+ if (refusal !== null)
208
+ return { kind: "refused", refusal };
209
+ arms.push({ kind: "ordinary", path: worktree.path });
226
210
  }
227
- if (input.coordinates.workspace === "here" && hereSource === null)
228
- throw new Error("targeted here workspace is unappointed");
229
- if (hereSource !== null && !arms.some((arm) => arm.kind === "here"))
230
- throw new Error("targeted here workspace is not a registered checkout of its target");
231
211
  return { kind: "ready", arms };
232
212
  }
233
213
  /** Adjudicate the complete post-Verification audit target answer without placing. */
@@ -252,7 +232,7 @@ export async function adjudicateAuditTarget(repository, input) {
252
232
  return { kind: "failed", diagnostic: error.message };
253
233
  }
254
234
  }
255
- export async function prepareTargetPlacement(repository, state, target, hereWorkspacePath) {
235
+ export async function prepareTargetPlacement(repository, state, target) {
256
236
  if (state.coordinates.target !== target.target || state.currentIntegration?.snapshot !== target.newOid) {
257
237
  throw new Error("placement state does not match its offered target movement");
258
238
  }
@@ -261,7 +241,6 @@ export async function prepareTargetPlacement(repository, state, target, hereWork
261
241
  coordinates: { ...state.coordinates, target: target.target },
262
242
  predecessor: target.expectedOid,
263
243
  candidate: target.newOid,
264
- ...(hereWorkspacePath === undefined ? {} : { hereWorkspacePath }),
265
244
  });
266
245
  return observation.kind === "refused"
267
246
  ? observation
@@ -274,10 +253,7 @@ export async function followTargetPlacement(repository, prepared) {
274
253
  const candidate = gitObjectIdForSnapshot(prepared.target.newOid);
275
254
  for (const arm of prepared.arms) {
276
255
  try {
277
- if (arm.kind === "here")
278
- await runGit(repository, ["-C", arm.path, "read-tree", candidate]);
279
- else
280
- await runGit(repository, ["-C", arm.path, "read-tree", "-m", "-u", predecessor, candidate]);
256
+ await runGit(repository, ["-C", arm.path, "read-tree", "-m", "-u", predecessor, candidate]);
281
257
  effects.push({ kind: "target-checkout", path: arm.path, target: prepared.target.target, action: "followed" });
282
258
  }
283
259
  catch (error) {
@@ -294,22 +270,46 @@ export async function followTargetPlacement(repository, prepared) {
294
270
  function recoveryLag(path, target, detail) {
295
271
  return { kind: "target-checkout-retained", path, target, diagnostic: detail };
296
272
  }
297
- async function recoverCheckout(input) {
298
- const { repository, path, predecessor, candidate, predecessorTree, candidateTree } = input;
273
+ async function classifyCheckout(input) {
274
+ const { repository, path, predecessorTree, candidateTree } = input;
299
275
  const changedPaths = await gitPaths(repository, path, ["diff", "--name-only", "-z", predecessorTree, candidateTree]);
300
276
  if (changedPaths.length === 0)
301
- return "complete";
277
+ return { kind: "complete" };
302
278
  const workspaceTree = (await captureWorkspaceTree(repository, path)).tree;
303
279
  const candidateIndex = await indexMatchesTreeOnPaths(repository, path, candidateTree, changedPaths);
304
280
  const candidateWorkspace = await workspaceMatchesTreeOnPaths(repository, path, candidateTree, workspaceTree, changedPaths);
305
281
  if (candidateIndex && candidateWorkspace)
282
+ return { kind: "complete" };
283
+ if (workspaceTree === candidateTree)
284
+ return { kind: "recoverable", action: "candidate-index" };
285
+ const predecessorIndex = await indexMatchesTreeOnPaths(repository, path, predecessorTree, changedPaths);
286
+ if (predecessorIndex && candidateWorkspace)
287
+ return { kind: "recoverable", action: "index-merge" };
288
+ return !predecessorIndex ||
289
+ !(await workspaceMatchesTreeOnPaths(repository, path, predecessorTree, workspaceTree, changedPaths))
290
+ ? { kind: "retained" }
291
+ : { kind: "recoverable", action: "worktree-merge" };
292
+ }
293
+ /** Judge the target-checkout shape without mutating refs, worktrees, or indexes. */
294
+ export async function observeTargetCheckoutShape(repository, input) {
295
+ const [predecessorTree, candidateTree] = await Promise.all([
296
+ commitTree(repository, input.predecessor),
297
+ commitTree(repository, input.candidate),
298
+ ]);
299
+ return (await classifyCheckout({ repository, ...input, predecessorTree, candidateTree })).kind;
300
+ }
301
+ async function recoverCheckout(input) {
302
+ const classified = await classifyCheckout(input);
303
+ if (classified.kind === "complete")
306
304
  return "complete";
307
- if (workspaceTree === candidateTree) {
305
+ if (classified.kind === "retained")
306
+ return "retained";
307
+ const { repository, path, predecessor, candidate } = input;
308
+ if (classified.action === "candidate-index") {
308
309
  await runGit(repository, ["-C", path, "read-tree", gitObjectIdForSnapshot(candidate)]);
309
310
  return "recovered";
310
311
  }
311
- const predecessorIndex = await indexMatchesTreeOnPaths(repository, path, predecessorTree, changedPaths);
312
- if (predecessorIndex && candidateWorkspace) {
312
+ if (classified.action === "index-merge") {
313
313
  await runGit(repository, [
314
314
  "-C",
315
315
  path,
@@ -321,9 +321,6 @@ async function recoverCheckout(input) {
321
321
  ]);
322
322
  return "recovered";
323
323
  }
324
- const predecessorWorkspace = await workspaceMatchesTreeOnPaths(repository, path, predecessorTree, workspaceTree, changedPaths);
325
- if (!predecessorIndex || !predecessorWorkspace)
326
- return "retained";
327
324
  await runGit(repository, [
328
325
  "-C",
329
326
  path,
@@ -341,7 +338,7 @@ export async function recoverTargetPlacement(repository, state) {
341
338
  if (state.terminal?.kind !== "claimed" || target === undefined || delivery === null)
342
339
  return { effects: [], lag: [] };
343
340
  const integration = state.currentIntegration;
344
- if (integration === null || await readRef(repository, target) !== integration.snapshot)
341
+ if (integration === null || (await readRef(repository, target)) !== integration.snapshot)
345
342
  return { effects: [], lag: [] };
346
343
  const candidateTree = await commitTree(repository, integration.snapshot);
347
344
  const predecessorTree = await commitTree(repository, integration.predecessor);
@@ -1,46 +1,20 @@
1
1
  import { access } from "node:fs/promises";
2
2
  import { gitObjectIdForSnapshot, mintSnapshotId } from "./identity.js";
3
- import { decodeGitNumstat, registeredWorktreePaths, } from "./repository.js";
4
- import { GitPlumbingError, runGit, runGitWithEnvironment, } from "./process.js";
5
- import { captureWorkspaceTree, worktreePath } from "./workspace.js";
6
- async function workspaceExists(repository, workspace, path) {
3
+ import { decodeGitNumstat, registeredWorktreePaths } from "./repository.js";
4
+ import { GitPlumbingError, runGit, runGitWithEnvironment } from "./process.js";
5
+ import { captureWorkspaceTree, unmergedWorkspacePaths, workspaceMergeHead, worktreePath } from "./workspace.js";
6
+ async function workspaceExists(repository, path) {
7
7
  const exists = await access(path).then(() => true, () => false);
8
- return workspace === "here"
9
- || (exists && (await registeredWorktreePaths(repository)).includes(path));
8
+ return exists && (await registeredWorktreePaths(repository)).includes(path);
10
9
  }
11
10
  function workspaceFor(repository, input) {
12
11
  if (input.workspacePath !== undefined)
13
12
  return input.workspacePath;
14
13
  return input.place === undefined ? undefined : worktreePath(repository, input.place);
15
14
  }
16
- async function workspaceMergeHead(repository, workspace) {
17
- try {
18
- return mintSnapshotId((await runGit(repository, ["-C", workspace, "rev-parse", "-q", "--verify", "MERGE_HEAD"]))
19
- .toString("utf8")
20
- .trim());
21
- }
22
- catch (error) {
23
- if (error instanceof GitPlumbingError && error.status === 1)
24
- return undefined;
25
- throw error;
26
- }
27
- }
28
- async function unmergedWorkspacePaths(repository, workspace) {
29
- const records = (await runGit(repository, ["-C", workspace, "ls-files", "--unmerged", "-z"]))
30
- .toString("utf8")
31
- .split("\0");
32
- const paths = new Set();
33
- for (const record of records.slice(0, -1)) {
34
- const separator = record.indexOf("\t");
35
- if (separator < 0)
36
- throw new Error(`malformed unmerged index record: ${record}`);
37
- paths.add(record.slice(separator + 1));
38
- }
39
- return [...paths].sort();
40
- }
41
15
  export async function captureTender(repository, input) {
42
16
  const workspace = workspaceFor(repository, input);
43
- if (workspace === undefined || !(await workspaceExists(repository, input.coordinates.workspace, workspace))) {
17
+ if (workspace === undefined || !(await workspaceExists(repository, workspace))) {
44
18
  return { kind: "refused", refusal: { kind: "worktree-missing", contractId: input.contractId } };
45
19
  }
46
20
  if (input.rejectUnmerged === true) {
@@ -49,22 +23,14 @@ export async function captureTender(repository, input) {
49
23
  return { kind: "refused", refusal: { kind: "unmerged-paths", contractId: input.contractId, paths } };
50
24
  }
51
25
  const captured = await captureWorkspaceTree(repository, workspace);
52
- const mergeHead = input.captureMergeState === true
53
- ? await workspaceMergeHead(repository, workspace)
54
- : undefined;
26
+ const mergeHead = input.captureMergeState === true ? await workspaceMergeHead(repository, workspace) : undefined;
55
27
  return {
56
28
  kind: "prepared",
57
29
  data: { ...captured, ...(mergeHead === undefined ? {} : { mergeHead }) },
58
30
  };
59
31
  }
60
32
  async function dirtyShortStat(repository, tender) {
61
- return decodeGitNumstat(await runGit(repository, [
62
- "diff",
63
- "--numstat",
64
- "-z",
65
- gitObjectIdForSnapshot(tender.head),
66
- tender.tree,
67
- ]));
33
+ return decodeGitNumstat(await runGit(repository, ["diff", "--numstat", "-z", gitObjectIdForSnapshot(tender.head), tender.tree]));
68
34
  }
69
35
  export async function dirtyTenderRefusal(repository, contractId, tender) {
70
36
  return {
@@ -104,9 +70,8 @@ export async function prepareDeliveryCommitMetadata(repository, input) {
104
70
  effectiveConfig(repository, "user.name"),
105
71
  effectiveConfig(repository, "user.email"),
106
72
  ]);
107
- identity = name !== undefined && email !== undefined
108
- ? { name, email }
109
- : { name: "Keiyaku", email: "keiyaku@localhost" };
73
+ identity =
74
+ name !== undefined && email !== undefined ? { name, email } : { name: "Keiyaku", email: "keiyaku@localhost" };
110
75
  }
111
76
  const subject = input.message ?? `${input.contractId}: ${input.title}`;
112
77
  return {
@@ -129,6 +94,8 @@ export async function materializeTenderSnapshot(repository, tender, commit) {
129
94
  GIT_COMMITTER_EMAIL: commit.identity.email,
130
95
  GIT_AUTHOR_DATE: commit.at,
131
96
  GIT_COMMITTER_DATE: commit.at,
132
- })).toString("utf8").trim();
97
+ }))
98
+ .toString("utf8")
99
+ .trim();
133
100
  return mintSnapshotId(oid);
134
101
  }
@@ -0,0 +1,21 @@
1
+ import type { ContractState, SnapshotId } from "../core/facts/types.js";
2
+ import type { GitDecodeChannel } from "./read-observation.js";
3
+ import { type WorktreeHooks } from "./hooks.js";
4
+ import { type GitRepository } from "./process.js";
5
+ import type { Effect, ReconcileAccumulation, ReconcileResult, WorktreeTopology } from "./reconcile.js";
6
+ type TerminalReconcileInput = Readonly<{
7
+ repository: GitRepository;
8
+ channel: GitDecodeChannel;
9
+ hooks: WorktreeHooks;
10
+ retryHooks: boolean;
11
+ retainTerminalWorktree?: boolean;
12
+ place?: string;
13
+ }>;
14
+ type TerminalCustodyRefs = Readonly<{
15
+ ref: string;
16
+ pin: string;
17
+ }>;
18
+ export declare function updateRef(repository: GitRepository, ref: string, desired: SnapshotId): Promise<Effect>;
19
+ export declare function removeRef(repository: GitRepository, ref: string): Promise<Effect>;
20
+ export declare function reconcileTerminalManagedWorktree({ repository, channel, hooks, retryHooks, retainTerminalWorktree, place }: TerminalReconcileInput, state: ContractState, topology: WorktreeTopology, acc: ReconcileAccumulation, { ref, pin }: TerminalCustodyRefs): Promise<ReconcileResult>;
21
+ export {};