@astrosheep/keiyaku 2.9.2 → 2.9.4

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 (238) hide show
  1. package/build/.tsbuildinfo +1 -1
  2. package/build/agents/call-terms.js +13 -94
  3. package/build/agents/harness/activity-values.js +16 -0
  4. package/build/agents/harness/event-persistence.js +218 -0
  5. package/build/agents/harness/execution-handle.js +25 -222
  6. package/build/agents/harness/index.js +3 -1
  7. package/build/agents/harness/outcome.js +47 -4
  8. package/build/agents/harness/projection.js +42 -59
  9. package/build/agents/harness/provider-adapter.js +58 -0
  10. package/build/agents/harness/router.js +11 -187
  11. package/build/agents/harness/runtime.js +17 -4
  12. package/build/agents/harness/session.js +15 -0
  13. package/build/agents/launch-snapshot/model.js +15 -236
  14. package/build/agents/launch-snapshot/parse-helpers.js +114 -0
  15. package/build/agents/launch-snapshot/resolve.js +17 -43
  16. package/build/agents/opencode-sdk.js +45 -12
  17. package/build/agents/providers/claude-agent-sdk/adapter.js +6 -17
  18. package/build/agents/providers/claude-agent-sdk/registration.js +214 -0
  19. package/build/agents/providers/claude-agent-sdk/session-schema.js +2 -0
  20. package/build/agents/providers/codex-app-server/adapter.js +50 -53
  21. package/build/agents/providers/codex-app-server/events.js +67 -11
  22. package/build/agents/providers/codex-app-server/identity.js +1 -0
  23. package/build/agents/providers/codex-app-server/policy.js +29 -0
  24. package/build/agents/providers/codex-app-server/registration.js +248 -0
  25. package/build/agents/providers/codex-app-server/session-schema.js +6 -0
  26. package/build/agents/providers/codex-app-server/session.js +23 -17
  27. package/build/agents/providers/codex-app-server/terms.js +1 -0
  28. package/build/agents/providers/opencode-sdk/adapter.js +94 -50
  29. package/build/agents/providers/opencode-sdk/events.js +8 -4
  30. package/build/agents/providers/opencode-sdk/registration.js +135 -0
  31. package/build/agents/providers/opencode-sdk/session-schema.js +2 -0
  32. package/build/agents/providers/opencode-sdk/session.js +21 -14
  33. package/build/agents/providers/opencode-sdk/terms.js +1 -0
  34. package/build/agents/providers/pi/adapter.js +36 -30
  35. package/build/agents/providers/pi/registration.js +145 -0
  36. package/build/agents/providers/pi/session-schema.js +6 -0
  37. package/build/agents/providers/pi/terms.js +1 -0
  38. package/build/agents/providers/registration.js +7 -0
  39. package/build/agents/providers/registry.js +76 -0
  40. package/build/agents/selector.js +2 -29
  41. package/build/agents/types.js +8 -0
  42. package/build/artifact-directories.js +22 -0
  43. package/build/cli/commands/akuma/akuma/handler.js +1 -1
  44. package/build/cli/commands/akuma/view/handler.js +5 -4
  45. package/build/cli/commands/akuma.js +6 -0
  46. package/build/cli/commands/contract/amend/handler.js +8 -2
  47. package/build/cli/commands/contract/arc/handler.js +7 -1
  48. package/build/cli/commands/contract/bind/handler.js +4 -3
  49. package/build/cli/commands/contract/bind/meta.js +34 -7
  50. package/build/cli/commands/contract/forfeit/handler.js +4 -4
  51. package/build/cli/commands/contract/log/handler.js +3 -3
  52. package/build/cli/commands/contract/petition/handler.js +4 -6
  53. package/build/cli/commands/contract/renew/handler.js +20 -6
  54. package/build/cli/commands/contract/renew/meta.js +5 -4
  55. package/build/cli/commands/metadata.js +58 -0
  56. package/build/cli/commands/projection/call/handler.js +6 -5
  57. package/build/cli/commands/projection/kill/handler.js +16 -32
  58. package/build/cli/commands/projection/shared.js +28 -0
  59. package/build/cli/commands/projection/status/handler.js +1 -1
  60. package/build/cli/commands/projection/tell/handler.js +14 -30
  61. package/build/cli/commands/projection/wait/handler.js +45 -22
  62. package/build/cli/commands/projection/wait/meta.js +7 -2
  63. package/build/cli/commands/registry.js +2 -54
  64. package/build/cli/commands/shared.js +10 -7
  65. package/build/cli/commands/skills/install/handler.js +2 -5
  66. package/build/cli/commands/skills/skills/handler.js +1 -1
  67. package/build/cli/commands/task/add/handler.js +5 -13
  68. package/build/cli/commands/task/catalog.js +2 -2
  69. package/build/cli/commands/task/done/handler.js +4 -8
  70. package/build/cli/commands/task/drop/handler.js +3 -6
  71. package/build/cli/commands/task/ls/handler.js +3 -5
  72. package/build/cli/commands/task/shared.js +14 -4
  73. package/build/cli/commands/task/{view → show}/handler.js +7 -8
  74. package/build/cli/commands/task/show/meta.js +8 -0
  75. package/build/cli/commands/task/start/handler.js +3 -6
  76. package/build/cli/commands/task/stop/handler.js +4 -8
  77. package/build/cli/commands/task/task/handler.js +1 -1
  78. package/build/cli/commands/task/update/handler.js +4 -8
  79. package/build/cli/completion.js +1 -1
  80. package/build/cli/flags.js +19 -3
  81. package/build/cli/help.js +4 -11
  82. package/build/cli/index.js +57 -83
  83. package/build/cli/missing-subcommand.js +7 -0
  84. package/build/cli/parse-flags.js +14 -5
  85. package/build/cli/parse-metadata.js +3 -3
  86. package/build/cli/parse-selectors.js +1 -1
  87. package/build/cli/parse.js +21 -0
  88. package/build/cli/projection-address.js +7 -12
  89. package/build/cli/render/address.js +41 -37
  90. package/build/cli/render/arc.js +24 -12
  91. package/build/cli/render/call.js +11 -5
  92. package/build/cli/render/errors.js +36 -1
  93. package/build/cli/render/format.js +4 -0
  94. package/build/cli/render/kanshi.js +6 -12
  95. package/build/cli/render/kill.js +9 -0
  96. package/build/cli/render/misc.js +55 -12
  97. package/build/cli/render/petition.js +20 -15
  98. package/build/cli/render/projection-activity.js +3 -0
  99. package/build/cli/render/shared.js +60 -21
  100. package/build/cli/render/skills.js +10 -0
  101. package/build/cli/render/status.js +37 -14
  102. package/build/cli/render/success-response.js +77 -0
  103. package/build/cli/render/task.js +54 -14
  104. package/build/cli/render/tell.js +5 -0
  105. package/build/cli/render/terminal-failure.js +58 -0
  106. package/build/cli/render/types.js +1 -0
  107. package/build/cli/render/wait.js +38 -36
  108. package/build/cli/types.js +1 -1
  109. package/build/config/akuma-loader.js +0 -5
  110. package/build/config/env-keys.js +1 -1
  111. package/build/config/provider-policy-ownership.js +3 -32
  112. package/build/config/settings/disease.js +8 -1
  113. package/build/config/settings/loader.js +11 -3
  114. package/build/config/settings/provider-instance-fields.js +51 -0
  115. package/build/config/settings/schema.js +6 -176
  116. package/build/core/addressing.js +66 -16
  117. package/build/core/amend.js +135 -18
  118. package/build/core/arc.js +19 -67
  119. package/build/core/bind.js +82 -35
  120. package/build/core/call/call.js +15 -9
  121. package/build/core/call/context.js +6 -51
  122. package/build/core/call/execution.js +21 -22
  123. package/build/core/call/prompt.js +14 -1
  124. package/build/core/command-markdown.js +45 -0
  125. package/build/core/context.js +13 -7
  126. package/build/core/contract-view.js +20 -3
  127. package/build/core/draft.js +123 -16
  128. package/build/core/forfeit.js +5 -2
  129. package/build/core/hints.js +1 -75
  130. package/build/core/identifier-slug.js +66 -0
  131. package/build/core/ids.js +25 -11
  132. package/build/core/ledger.js +20 -68
  133. package/build/core/markdown/parser.js +0 -68
  134. package/build/core/markdown/render.js +68 -0
  135. package/build/core/markdown/sections.js +2 -1
  136. package/build/core/outcome-base.js +4 -0
  137. package/build/core/projection/generation/database.js +2 -38
  138. package/build/core/projection/generation/model.js +42 -1
  139. package/build/core/{projection-generation-continuation.js → projection/generation/projection-generation-continuation.js} +3 -3
  140. package/build/core/{projection-generation-execution.js → projection/generation/projection-generation-execution.js} +19 -49
  141. package/build/core/{projection-generation-identity.js → projection/generation/projection-generation-identity.js} +2 -2
  142. package/build/core/{projection-generation-launcher.js → projection/generation/projection-generation-launcher.js} +5 -3
  143. package/build/core/{projection-generation-process.js → projection/generation/projection-generation-process.js} +2 -1
  144. package/build/core/{projection-generation-runner.js → projection/generation/projection-generation-runner.js} +18 -3
  145. package/build/core/projection/generation/store.js +2 -2
  146. package/build/core/projection/generation/transitions.js +1 -1
  147. package/build/core/projection/index.js +27 -0
  148. package/build/core/{projection-activity.js → projection/projection-activity.js} +12 -0
  149. package/build/core/{projection-alias.js → projection/projection-alias.js} +14 -19
  150. package/build/core/projection/projection-coordinate.js +61 -0
  151. package/build/core/{projection-core.js → projection/projection-core.js} +9 -9
  152. package/build/core/{projection-execution-observer.js → projection/projection-execution-observer.js} +2 -57
  153. package/build/core/{projection-identity.js → projection/projection-identity.js} +1 -1
  154. package/build/core/{projection-kill.js → projection/projection-kill.js} +1 -1
  155. package/build/core/{projection-life-observer.js → projection/projection-life-observer.js} +1 -1
  156. package/build/core/{projection-mint.js → projection/projection-mint.js} +10 -10
  157. package/build/core/{projection-status.js → projection/projection-status.js} +98 -25
  158. package/build/core/projection/projection-terminal-failure.js +104 -0
  159. package/build/core/projection/projection-wait.js +278 -0
  160. package/build/core/projection/projection-wake.js +203 -0
  161. package/build/core/provider-loader-ports.js +4 -0
  162. package/build/core/ref-log.js +65 -40
  163. package/build/core/registry.js +63 -10
  164. package/build/core/renew-session.js +128 -0
  165. package/build/core/renew.js +426 -99
  166. package/build/core/scope.js +143 -66
  167. package/build/core/seal.js +139 -35
  168. package/build/core/{claim-delivery.js → settlement/claim-delivery.js} +9 -14
  169. package/build/core/{claim.js → settlement/claim.js} +32 -8
  170. package/build/core/settlement/index.js +19 -0
  171. package/build/core/{petition-claim-gates.js → settlement/petition-claim-gates.js} +22 -18
  172. package/build/core/{petition-forfeit.js → settlement/petition-forfeit.js} +2 -2
  173. package/build/core/{petition-head-guard.js → settlement/petition-head-guard.js} +2 -2
  174. package/build/core/settlement/petition-io.js +1 -0
  175. package/build/core/{petition-preview.js → settlement/petition-preview.js} +1 -1
  176. package/build/core/{petition.js → settlement/petition.js} +12 -10
  177. package/build/core/settlement/queue-read-model.js +143 -0
  178. package/build/core/settlement/queue-seat-allocation.js +156 -0
  179. package/build/core/settlement/queue.js +6 -0
  180. package/build/core/settlement/settlement.js +295 -0
  181. package/build/core/{verdict.js → settlement/verdict.js} +9 -37
  182. package/build/core/status/board.js +24 -23
  183. package/build/core/status/ledger-batch.js +158 -0
  184. package/build/core/status/reconciliation.js +10 -4
  185. package/build/core/stored-agent-event.js +3 -0
  186. package/build/core/task/board.js +40 -4
  187. package/build/core/task/commands.js +30 -21
  188. package/build/core/task/index.js +12 -0
  189. package/build/core/task/settlement-git.js +157 -65
  190. package/build/core/task/settlement-policy.js +54 -24
  191. package/build/core/task/task-contract.js +370 -0
  192. package/build/core/{task-doctor.js → task/task-doctor.js} +1 -1
  193. package/build/core/{task-git-actor.js → task/task-git-actor.js} +5 -10
  194. package/build/core/{task-git-runtime.js → task/task-git-runtime.js} +3 -3
  195. package/build/core/{task-git-store.js → task/task-git-store.js} +92 -29
  196. package/build/core/task/task-repository.js +1 -0
  197. package/build/core/{task-store-repository.js → task/task-store-repository.js} +5 -4
  198. package/build/core/task/task-store.js +1 -0
  199. package/build/core/{task-worktree.js → task/task-worktree.js} +2 -1
  200. package/build/core/task/task.js +5 -0
  201. package/build/core/task/tree.js +37 -0
  202. package/build/core/task/validation.js +5 -2
  203. package/build/core/transcripts.js +15 -3
  204. package/build/core/worktree-path.js +43 -28
  205. package/build/flow-error.js +85 -39
  206. package/build/fs/durability.js +27 -0
  207. package/build/fs/path-coordinate.js +16 -0
  208. package/build/generated/version.js +3 -2
  209. package/build/git/branches.js +2 -1
  210. package/build/git/core.js +106 -2
  211. package/build/git/path-coordinate.js +17 -0
  212. package/build/git/refs.js +57 -0
  213. package/build/git/streaming-batch.js +301 -0
  214. package/build/git/worktree.js +65 -0
  215. package/build/index.js +3 -3
  216. package/build/keiyaku.js +2 -36
  217. package/package.json +4 -10
  218. package/skills/keiyaku/SKILL.md +3 -2
  219. package/skills/keiyaku-akuma/SKILL.md +10 -6
  220. package/skills/keiyaku-task/SKILL.md +1 -1
  221. package/skills/keiyaku-workflow/SKILL.md +25 -4
  222. package/build/agents/providers/codex-sdk.js +0 -329
  223. package/build/cli/commands/task/view/meta.js +0 -8
  224. package/build/core/command-io.js +0 -174
  225. package/build/core/path-coordinate.js +0 -27
  226. package/build/core/petition-claim.js +0 -117
  227. package/build/core/petition-run.js +0 -83
  228. package/build/core/projection-coordinate.js +0 -63
  229. package/build/core/projection-wait.js +0 -165
  230. package/build/core/projection-wake.js +0 -136
  231. package/build/core/queue.js +0 -343
  232. package/build/core/task-contract.js +0 -237
  233. package/build/core/task.js +0 -4
  234. /package/build/{core/task-repository.js → agents/launch-snapshot/types.js} +0 -0
  235. /package/build/{core/task-store.js → agents/providers/claude-agent-sdk/terms.js} +0 -0
  236. /package/build/core/{projection-generation-runtime.js → projection/generation/projection-generation-runtime.js} +0 -0
  237. /package/build/core/{projection-life-protocol.js → projection/projection-life-protocol.js} +0 -0
  238. /package/build/core/{projection-runner-lock.js → projection/projection-runner-lock.js} +0 -0
@@ -1,343 +0,0 @@
1
- import { createHash } from "node:crypto";
2
- import { FlowError } from "../flow-error.js";
3
- import { createGit, errorContainsAnyPattern, MISSING_HEAD_PATTERNS, wrapGitError } from "../git/core.js";
4
- import { listContractIds, readLedger } from "./ledger.js";
5
- import { deriveContractState } from "./status/lifecycle.js";
6
- import { commissionTarget } from "./status/drift.js";
7
- const EMPTY_TREE_HASH = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
8
- const MISSING_REF_SENTINEL = "0".repeat(40);
9
- const ISO_UTC_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/;
10
- function isPositiveSafeInteger(value) {
11
- return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
12
- }
13
- function isCurrentPetitionData(data) {
14
- return "target" in data && "seat" in data;
15
- }
16
- function latestPetition(entries) {
17
- for (let index = entries.length - 1; index >= 0; index -= 1) {
18
- const entry = entries[index];
19
- if (entry.kind === "petition")
20
- return entry;
21
- }
22
- return undefined;
23
- }
24
- export function deriveQueueReadModel(ledgers) {
25
- const seatsByContract = new Map();
26
- const diagnosticsByContract = new Map();
27
- const orderedByTarget = new Map();
28
- for (const ledger of ledgers) {
29
- if (deriveContractState(ledger.entries).state !== "petitioned")
30
- continue;
31
- const petition = latestPetition(ledger.entries);
32
- if (!petition)
33
- continue;
34
- const derived = commissionTarget(ledger.entries);
35
- if (!derived)
36
- continue;
37
- const diagnostics = [];
38
- let seat;
39
- if (isCurrentPetitionData(petition.data)) {
40
- seat = {
41
- contract: ledger.contractId,
42
- target: petition.data.target,
43
- seat: petition.data.seat,
44
- legacy: false,
45
- };
46
- if (petition.data.target !== derived.ref) {
47
- diagnostics.push({
48
- kind: "target-mismatch",
49
- petitioned: petition.data.target,
50
- derived: derived.ref,
51
- });
52
- }
53
- const bucket = orderedByTarget.get(petition.data.target) ?? [];
54
- bucket.push(seat);
55
- orderedByTarget.set(petition.data.target, bucket);
56
- }
57
- else {
58
- seat = {
59
- contract: ledger.contractId,
60
- target: derived.ref,
61
- seat: null,
62
- legacy: true,
63
- };
64
- diagnostics.push({ kind: "legacy-petition" });
65
- }
66
- seatsByContract.set(ledger.contractId, seat);
67
- diagnosticsByContract.set(ledger.contractId, diagnostics);
68
- }
69
- for (const [target, seats] of orderedByTarget) {
70
- const bySeat = new Map();
71
- for (const seat of seats) {
72
- if (seat.seat === null)
73
- continue;
74
- const group = bySeat.get(seat.seat) ?? [];
75
- group.push(seat);
76
- bySeat.set(seat.seat, group);
77
- }
78
- for (const [seatNumber, group] of bySeat) {
79
- if (group.length < 2)
80
- continue;
81
- const sorted = [...group].sort((a, b) => a.contract.localeCompare(b.contract));
82
- for (const member of sorted) {
83
- const others = sorted.filter((other) => other.contract !== member.contract);
84
- const existing = diagnosticsByContract.get(member.contract) ?? [];
85
- for (const other of others) {
86
- existing.push({ kind: "duplicate-seat", seat: seatNumber, with: other.contract });
87
- }
88
- diagnosticsByContract.set(member.contract, existing);
89
- }
90
- }
91
- seats.sort((a, b) => {
92
- const seatA = a.seat ?? Number.POSITIVE_INFINITY;
93
- const seatB = b.seat ?? Number.POSITIVE_INFINITY;
94
- if (seatA !== seatB)
95
- return seatA - seatB;
96
- return a.contract.localeCompare(b.contract);
97
- });
98
- orderedByTarget.set(target, seats);
99
- }
100
- const targetQueues = [...orderedByTarget.entries()]
101
- .sort(([a], [b]) => a.localeCompare(b))
102
- .map(([target, ordered]) => ({ target, ordered }));
103
- return {
104
- targetQueues,
105
- seatsByContract,
106
- diagnosticsByContract,
107
- };
108
- }
109
- export async function readQueueReadModel(cwd) {
110
- const contractIds = await listContractIds(cwd);
111
- const ledgers = [];
112
- for (const id of contractIds) {
113
- const ledger = await readLedger(cwd, id);
114
- if (ledger)
115
- ledgers.push(ledger);
116
- }
117
- return deriveQueueReadModel(ledgers);
118
- }
119
- export function queueMembership(model, contractId) {
120
- const seat = model.seatsByContract.get(contractId);
121
- if (!seat)
122
- return undefined;
123
- const diagnostics = model.diagnosticsByContract.get(contractId) ?? [];
124
- if (seat.legacy) {
125
- return {
126
- seat,
127
- position: null,
128
- queueLength: 0,
129
- queueHead: null,
130
- diagnostics,
131
- };
132
- }
133
- const targetQueue = model.targetQueues.find((queue) => queue.target === seat.target);
134
- if (!targetQueue) {
135
- return {
136
- seat,
137
- position: null,
138
- queueLength: 0,
139
- queueHead: null,
140
- diagnostics,
141
- };
142
- }
143
- const index = targetQueue.ordered.findIndex((entry) => entry.contract === contractId);
144
- const position = index < 0 ? null : index + 1;
145
- return {
146
- seat,
147
- position,
148
- queueLength: targetQueue.ordered.length,
149
- queueHead: targetQueue.ordered[0]?.contract ?? null,
150
- diagnostics,
151
- };
152
- }
153
- export function assertClaimQueueHead(model, contractId) {
154
- const membership = queueMembership(model, contractId);
155
- if (!membership) {
156
- throw new FlowError("INTERNAL_STATE", `contract ${contractId} is not queue head; position=not queued`);
157
- }
158
- if (membership.seat.legacy) {
159
- throw new FlowError("QUEUE_LEGACY_PETITION", `contract ${contractId} petitioned before seat law took effect; reject its petition and petition again`);
160
- }
161
- const mismatch = membership.diagnostics.find((diagnostic) => diagnostic.kind === "target-mismatch");
162
- if (mismatch && mismatch.kind === "target-mismatch") {
163
- throw new FlowError("QUEUE_TARGET_MISMATCH", `contract ${contractId} petition target ${mismatch.petitioned} disagrees with derived target ${mismatch.derived}; reject its petition and petition again`);
164
- }
165
- if (membership.position !== 1) {
166
- throw new FlowError("INTERNAL_STATE", `contract ${contractId} is not queue head; position=${membership.position ?? "not queued"}`);
167
- }
168
- return membership;
169
- }
170
- export function seatAllocatorRef(target) {
171
- const digest = createHash("sha256").update(target, "utf8").digest("hex");
172
- return `refs/keiyaku/seat/${digest}`;
173
- }
174
- async function resolveRefHeadRaw(cwd, ref) {
175
- const git = createGit(cwd);
176
- try {
177
- // Raw observed-head read: any existing Git object id, not commit-only.
178
- // Non-commit heads are returned so parseAllocatorChain can classify them as corrupt.
179
- return (await git.raw(["rev-parse", "--verify", ref])).trim();
180
- }
181
- catch (error) {
182
- if (errorContainsAnyPattern(error, MISSING_HEAD_PATTERNS))
183
- return null;
184
- throw wrapGitError(`rev-parse ${ref}`, error, cwd);
185
- }
186
- }
187
- async function readCommitMessage(cwd, commit) {
188
- const git = createGit(cwd);
189
- try {
190
- return await git.raw(["show", "-s", "--format=%B", commit]);
191
- }
192
- catch (error) {
193
- throw wrapGitError(`show commit message ${commit}`, error, cwd);
194
- }
195
- }
196
- function parseSeatAllocatorMessage(message, expectedTarget) {
197
- const normalized = message.replace(/\r\n/g, "\n");
198
- const separatorIndex = normalized.indexOf("\n\n");
199
- if (separatorIndex < 0)
200
- throw new Error("missing blank line");
201
- const subject = normalized.slice(0, separatorIndex).trim();
202
- const body = normalized.slice(separatorIndex + 2).trim();
203
- const parsed = JSON.parse(body);
204
- const keys = Object.keys(parsed).sort();
205
- if (keys.join(",") !== "actor,at,seat,target,v")
206
- throw new Error("unexpected fields");
207
- if (parsed.v !== 1)
208
- throw new Error("bad version");
209
- if (typeof parsed.target !== "string" || parsed.target !== expectedTarget)
210
- throw new Error("target mismatch");
211
- if (!isPositiveSafeInteger(parsed.seat))
212
- throw new Error("bad seat");
213
- if (typeof parsed.at !== "string" || !ISO_UTC_RE.test(parsed.at))
214
- throw new Error("bad at");
215
- if (typeof parsed.actor !== "string" || parsed.actor.trim().length === 0)
216
- throw new Error("bad actor");
217
- if (subject !== `keiyaku seat: ${parsed.seat}`)
218
- throw new Error("bad subject");
219
- return {
220
- v: 1,
221
- target: parsed.target,
222
- seat: parsed.seat,
223
- at: parsed.at,
224
- actor: parsed.actor,
225
- };
226
- }
227
- async function parseAllocatorChain(cwd, head, expectedTarget) {
228
- const git = createGit(cwd);
229
- let output;
230
- try {
231
- output = await git.raw(["rev-list", "--reverse", head]);
232
- }
233
- catch {
234
- return null;
235
- }
236
- const commits = output
237
- .trim()
238
- .split("\n")
239
- .map((line) => line.trim())
240
- .filter(Boolean);
241
- if (commits.length === 0)
242
- return null;
243
- const entries = [];
244
- let previousSeat = 0;
245
- for (const commit of commits) {
246
- try {
247
- const entry = parseSeatAllocatorMessage(await readCommitMessage(cwd, commit), expectedTarget);
248
- if (entry.seat <= previousSeat)
249
- return null;
250
- previousSeat = entry.seat;
251
- entries.push(entry);
252
- }
253
- catch {
254
- return null;
255
- }
256
- }
257
- return { entries, lastSeat: previousSeat };
258
- }
259
- async function ledgerSeatFloor(cwd, target) {
260
- const contractIds = await listContractIds(cwd);
261
- let maxSeat = 0;
262
- for (const id of contractIds) {
263
- const ledger = await readLedger(cwd, id);
264
- if (!ledger)
265
- continue;
266
- for (const entry of ledger.entries) {
267
- if (entry.kind !== "petition")
268
- continue;
269
- if (!isCurrentPetitionData(entry.data))
270
- continue;
271
- if (entry.data.target !== target)
272
- continue;
273
- if (entry.data.seat > maxSeat)
274
- maxSeat = entry.data.seat;
275
- }
276
- }
277
- return maxSeat;
278
- }
279
- async function createSeatCommit(cwd, entry, parentHead) {
280
- const git = createGit(cwd);
281
- const subject = `keiyaku seat: ${entry.seat}`;
282
- const args = ["commit-tree", EMPTY_TREE_HASH];
283
- if (parentHead)
284
- args.push("-p", parentHead);
285
- args.push("-m", subject, "-m", JSON.stringify(entry, null, 2));
286
- try {
287
- return (await git.raw(args)).trim();
288
- }
289
- catch (error) {
290
- throw wrapGitError(`commit-tree seat ${entry.seat}`, error, cwd);
291
- }
292
- }
293
- async function casUpdateRef(cwd, ref, newHead, expectedOld) {
294
- const git = createGit(cwd);
295
- const oldValue = expectedOld ?? MISSING_REF_SENTINEL;
296
- try {
297
- await git.raw(["update-ref", ref, newHead, oldValue]);
298
- return true;
299
- }
300
- catch {
301
- return false;
302
- }
303
- }
304
- export async function allocateSettlementSeat(cwd, input) {
305
- const ref = seatAllocatorRef(input.target);
306
- const maxRetries = 3;
307
- for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
308
- const observedHead = await resolveRefHeadRaw(cwd, ref);
309
- const floor = await ledgerSeatFloor(cwd, input.target);
310
- const parsed = observedHead ? await parseAllocatorChain(cwd, observedHead, input.target) : null;
311
- const healthy = parsed !== null && parsed.lastSeat >= floor;
312
- let seat;
313
- let parent;
314
- let expectedOld;
315
- if (healthy && parsed) {
316
- seat = parsed.lastSeat + 1;
317
- parent = observedHead;
318
- expectedOld = observedHead;
319
- }
320
- else {
321
- // Missing, parse failure, target mismatch, non-monotonic, or stale-behind-ledger:
322
- // mint a detached no-parent root at floor+1. Leave the bad chain for Git GC.
323
- seat = floor + 1;
324
- parent = null;
325
- expectedOld = observedHead;
326
- }
327
- if (!isPositiveSafeInteger(seat)) {
328
- throw new FlowError("INTERNAL_STATE", `settlement seats exhausted for ${input.target}`);
329
- }
330
- const entry = {
331
- v: 1,
332
- target: input.target,
333
- seat,
334
- at: input.at,
335
- actor: input.actor,
336
- };
337
- const newHead = await createSeatCommit(cwd, entry, parent);
338
- const casOk = await casUpdateRef(cwd, ref, newHead, expectedOld);
339
- if (casOk)
340
- return { head: newHead, seat };
341
- }
342
- throw new FlowError("INTERNAL_STATE", `failed to allocate settlement seat for ${input.target}`);
343
- }
@@ -1,237 +0,0 @@
1
- import { FlowError } from "../flow-error.js";
2
- import { assertGitObservation, requireGitRepository } from "../git/branches.js";
3
- import { withLockHeldTaskBoard } from "./task-git-store.js";
4
- import { prepareTaskDocumentReplacements, taskRepositoryFromLockedBoard } from "./task-store-repository.js";
5
- import { containsCommit, createLandingCommit, createTaskBoardCommit, inspectCurrentBranch, publishForwardTaskBoardCommit, readableRefHead, reconcileTaskIndex, refHead, sameTaskSourceBytes, taskContractAtCommit, taskWritesFromSources, updateBranch, updateTargetRef, } from "./task/settlement-git.js";
6
- import { contractPointerDocument, doneTaskReplacement, reopenedTaskDocument, requireHealthySettlementBoard, requireSettledTask, selectReadyTaskForBind, taskForContract, } from "./task/settlement-policy.js";
7
- import { stableRepoRoot } from "./worktree-path.js";
8
- async function clearAttemptedPointer(input) {
9
- const current = requireSettledTask(await input.repository.read(), input.task.id);
10
- if (current.contractId === input.contractId) {
11
- await replaceTaskDocument(input.repository, contractPointerDocument(current, undefined));
12
- }
13
- }
14
- async function replaceTaskDocument(repository, replacement) {
15
- return repository.mutate(() => ({
16
- replacements: [replacement],
17
- select: (resulting) => requireSettledTask(resulting, replacement.id),
18
- }));
19
- }
20
- export async function settleTaskBoardForBind(input, deps = {}) {
21
- await requireGitRepository(input.cwd, "task bind");
22
- return withLockHeldTaskBoard(input.cwd, async (transaction) => {
23
- if (transaction.location.mode !== "git") {
24
- if (transaction.location.capability === "unavailable") {
25
- assertGitObservation({ capability: "unavailable" }, "task bind");
26
- }
27
- throw new FlowError("NOT_GIT_REPO", "task bind requires Git task storage");
28
- }
29
- const hubCwd = transaction.location.root;
30
- const repository = taskRepositoryFromLockedBoard(transaction);
31
- const branch = inspectCurrentBranch(hubCwd);
32
- const board = requireHealthySettlementBoard(await repository.read());
33
- const selected = input.taskId === undefined
34
- ? undefined
35
- : selectReadyTaskForBind(board, input.taskId);
36
- if (selected) {
37
- await replaceTaskDocument(repository, contractPointerDocument(selected, input.contractId));
38
- }
39
- const firstCommit = createTaskBoardCommit({
40
- cwd: hubCwd,
41
- gitDir: transaction.location.gitDir,
42
- root: transaction.location.root,
43
- parent: branch.head,
44
- sources: transaction.sources,
45
- subject: selected
46
- ? `Bind task ${selected.id} to contract ${input.contractId}`
47
- : `Flush task board for contract ${input.contractId}`,
48
- });
49
- try {
50
- await updateBranch({ cwd: hubCwd, ref: branch.ref, next: firstCommit, expected: branch.head }, deps);
51
- }
52
- catch (error) {
53
- if (selected) {
54
- await clearAttemptedPointer({ repository, task: selected, contractId: input.contractId });
55
- const concurrentHead = refHead(hubCwd, branch.ref);
56
- if (taskContractAtCommit(hubCwd, concurrentHead, selected.id) === input.contractId) {
57
- await publishForwardTaskBoardCommit({
58
- cwd: hubCwd,
59
- ref: branch.ref,
60
- gitDir: transaction.location.gitDir,
61
- root: transaction.location.root,
62
- sources: transaction.sources,
63
- subject: `Clear failed contract ${input.contractId} from task ${selected.id}`,
64
- }, deps);
65
- }
66
- }
67
- throw error;
68
- }
69
- try {
70
- reconcileTaskIndex(hubCwd, firstCommit);
71
- }
72
- catch (indexError) {
73
- if (selected) {
74
- await clearAttemptedPointer({ repository, task: selected, contractId: input.contractId });
75
- await publishForwardTaskBoardCommit({
76
- cwd: hubCwd,
77
- ref: branch.ref,
78
- gitDir: transaction.location.gitDir,
79
- root: transaction.location.root,
80
- sources: transaction.sources,
81
- subject: `Clear failed contract ${input.contractId} from task ${selected.id}`,
82
- }, deps);
83
- }
84
- throw indexError;
85
- }
86
- try {
87
- const ledger = await input.publishLedger(firstCommit);
88
- return {
89
- boardCommit: firstCommit,
90
- ledger,
91
- ...(selected ? { task: requireSettledTask(await repository.read(), selected.id) } : {}),
92
- };
93
- }
94
- catch (publishError) {
95
- if (!selected)
96
- throw publishError;
97
- await clearAttemptedPointer({ repository, task: selected, contractId: input.contractId });
98
- await publishForwardTaskBoardCommit({
99
- cwd: hubCwd,
100
- ref: branch.ref,
101
- gitDir: transaction.location.gitDir,
102
- root: transaction.location.root,
103
- sources: transaction.sources,
104
- subject: `Clear failed contract ${input.contractId} from task ${selected.id}`,
105
- }, deps);
106
- throw publishError;
107
- }
108
- });
109
- }
110
- export async function reconcileTaskForfeit(input) {
111
- await requireGitRepository(input.cwd, "task forfeit");
112
- const hubRoot = await stableRepoRoot(input.cwd);
113
- return withLockHeldTaskBoard(hubRoot, async (transaction) => {
114
- if (transaction.location.mode !== "git") {
115
- if (transaction.location.capability === "unavailable") {
116
- assertGitObservation({ capability: "unavailable" }, "task forfeit");
117
- }
118
- throw new FlowError("NOT_GIT_REPO", "task forfeit requires Git task storage");
119
- }
120
- const repository = taskRepositoryFromLockedBoard(transaction);
121
- const matched = taskForContract(await repository.read(), input.contractId);
122
- const ledger = await input.publishLedger();
123
- if (!matched)
124
- return { ledger };
125
- const task = await replaceTaskDocument(repository, reopenedTaskDocument(matched));
126
- return {
127
- ledger,
128
- task,
129
- };
130
- });
131
- }
132
- /**
133
- * Claim owns the sole task-board settlement after target CAS. Contract branch
134
- * task trees are fossils: the candidate is the code-only three-way merge plus
135
- * one complete snapshot of the common hub board held under its repository lock.
136
- */
137
- export async function settleTaskBoardForLanding(input) {
138
- await requireGitRepository(input.cwd, "task landing");
139
- const hubRoot = await stableRepoRoot(input.cwd);
140
- let lastTargetHead = input.targetHead;
141
- let lastPublishError;
142
- let acceptedMovement;
143
- for (let attempt = 0; attempt < 4; attempt += 1) {
144
- const result = await withLockHeldTaskBoard(hubRoot, async (transaction) => {
145
- if (transaction.location.mode !== "git") {
146
- if (transaction.location.capability === "unavailable") {
147
- assertGitObservation({ capability: "unavailable" }, "task landing");
148
- }
149
- throw new FlowError("NOT_GIT_REPO", "task landing requires Git task storage");
150
- }
151
- // Each attempt reads fresh target and board facts while holding the
152
- // common task lock. A losing CAS exits before the next attempt.
153
- const targetHead = refHead(input.cwd, input.targetRef);
154
- lastTargetHead = targetHead;
155
- const repository = taskRepositoryFromLockedBoard(transaction);
156
- const board = requireHealthySettlementBoard(await repository.read());
157
- const replacement = doneTaskReplacement(board, input.contractId);
158
- const prepared = replacement
159
- ? prepareTaskDocumentReplacements(transaction.sources, [replacement])
160
- : { sources: transaction.sources, board };
161
- const finalSources = prepared.sources;
162
- requireHealthySettlementBoard(prepared.board);
163
- let mergeCommit = targetHead;
164
- let movement = acceptedMovement?.mergeCommit === targetHead ? acceptedMovement : undefined;
165
- if (!containsCommit(input.cwd, input.deliveryHead, targetHead)) {
166
- mergeCommit = createLandingCommit({
167
- cwd: input.cwd,
168
- gitDir: transaction.location.gitDir,
169
- root: transaction.location.root,
170
- base: input.baseCommit,
171
- target: targetHead,
172
- delivery: input.deliveryHead,
173
- sources: finalSources,
174
- contractId: input.contractId,
175
- targetBranch: input.targetBranch,
176
- });
177
- await input.beforeCas?.(mergeCommit, targetHead);
178
- try {
179
- updateTargetRef(input.cwd, input.targetRef, mergeCommit, targetHead);
180
- movement = { mergeCommit, oldHead: targetHead };
181
- acceptedMovement = movement;
182
- }
183
- catch {
184
- const current = readableRefHead(input.cwd, input.targetRef);
185
- if (!current || !containsCommit(input.cwd, input.deliveryHead, current)) {
186
- return { kind: "retry" };
187
- }
188
- mergeCommit = current;
189
- movement = acceptedMovement?.mergeCommit === mergeCommit ? acceptedMovement : undefined;
190
- }
191
- }
192
- const warnings = [];
193
- if (!sameTaskSourceBytes(transaction.location.root, transaction.sources, finalSources)) {
194
- await transaction.replace(taskWritesFromSources(finalSources));
195
- }
196
- try {
197
- reconcileTaskIndex(transaction.location.root, mergeCommit);
198
- }
199
- catch (error) {
200
- warnings.push(`Accepted landing ${mergeCommit}, but task index reconciliation failed: ${error instanceof Error ? error.message : String(error)}`);
201
- }
202
- try {
203
- await input.publishLedger?.(mergeCommit);
204
- }
205
- catch (error) {
206
- lastPublishError = error;
207
- return { kind: "retry" };
208
- }
209
- const reconcileWarnings = movement
210
- ? await input.afterCas?.(mergeCommit, movement.oldHead)
211
- : undefined;
212
- warnings.push(...(reconcileWarnings ?? []));
213
- return {
214
- kind: movement ? "accepted" : "idempotent",
215
- mergeCommit,
216
- ...(movement ? { oldHead: movement.oldHead } : {}),
217
- ...(warnings.length > 0 ? { warnings } : {}),
218
- };
219
- });
220
- if (result.kind === "retry")
221
- continue;
222
- if (result.kind === "idempotent") {
223
- return {
224
- mergeCommit: result.mergeCommit,
225
- ...(result.warnings ? { warnings: result.warnings } : {}),
226
- };
227
- }
228
- return {
229
- mergeCommit: result.mergeCommit,
230
- oldHead: result.oldHead,
231
- ...(result.warnings ? { warnings: result.warnings } : {}),
232
- };
233
- }
234
- if (lastPublishError !== undefined)
235
- throw lastPublishError;
236
- throw new FlowError("CONCURRENT_MODIFICATION", `target branch kept moving while claiming ${input.contractId}; last observed ${lastTargetHead}`);
237
- }
@@ -1,4 +0,0 @@
1
- export { parseTaskDocument, serializeTaskDocument, } from "./task/document.js";
2
- export { resolveTaskReadId, } from "./task/board.js";
3
- export { buildTaskBoard } from "./task/source-board.js";
4
- export { readTaskBoard, createTask, updateTask, startTask, stopTask, doneTask, dropTask, selectSoleInProgress, rollbackOrphanTaskPointers, } from "./task/commands.js";