@astrosheep/keiyaku 2.9.3 → 2.9.5

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 (282) hide show
  1. package/build/.tsbuildinfo +1 -1
  2. package/build/agents/call-terms.js +14 -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 +26 -222
  6. package/build/agents/harness/index.js +3 -1
  7. package/build/agents/harness/outcome.js +68 -5
  8. package/build/agents/harness/projection.js +179 -124
  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 +82 -53
  21. package/build/agents/providers/codex-app-server/events.js +90 -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/events.js +4 -3
  36. package/build/agents/providers/pi/registration.js +145 -0
  37. package/build/agents/providers/pi/session-schema.js +6 -0
  38. package/build/agents/providers/pi/terms.js +1 -0
  39. package/build/agents/providers/registration.js +7 -0
  40. package/build/agents/providers/registry.js +76 -0
  41. package/build/agents/selector.js +2 -29
  42. package/build/agents/types.js +10 -0
  43. package/build/artifact-directories.js +22 -0
  44. package/build/cli/commands/akuma/akuma/handler.js +1 -1
  45. package/build/cli/commands/akuma/akuma/meta.js +1 -1
  46. package/build/cli/commands/akuma/list/meta-ls.js +1 -1
  47. package/build/cli/commands/akuma/view/handler.js +5 -4
  48. package/build/cli/commands/akuma/view/meta.js +1 -1
  49. package/build/cli/commands/akuma.js +6 -0
  50. package/build/cli/commands/contract/amend/handler.js +8 -2
  51. package/build/cli/commands/contract/amend/meta.js +4 -4
  52. package/build/cli/commands/contract/arc/handler.js +8 -2
  53. package/build/cli/commands/contract/arc/meta.js +4 -4
  54. package/build/cli/commands/contract/audit/handler.js +16 -0
  55. package/build/cli/commands/contract/audit/meta.js +16 -0
  56. package/build/cli/commands/contract/bind/handler.js +4 -3
  57. package/build/cli/commands/contract/bind/meta.js +44 -8
  58. package/build/cli/commands/contract/catalog.js +2 -0
  59. package/build/cli/commands/contract/forfeit/handler.js +4 -4
  60. package/build/cli/commands/contract/forfeit/meta.js +1 -1
  61. package/build/cli/commands/contract/log/handler.js +3 -3
  62. package/build/cli/commands/contract/log/meta.js +1 -1
  63. package/build/cli/commands/contract/petition/handler.js +5 -8
  64. package/build/cli/commands/contract/petition/meta.js +6 -6
  65. package/build/cli/commands/contract/renew/handler.js +20 -6
  66. package/build/cli/commands/contract/renew/meta.js +6 -5
  67. package/build/cli/commands/metadata.js +59 -0
  68. package/build/cli/commands/projection/call/handler.js +6 -5
  69. package/build/cli/commands/projection/call/meta.js +4 -1
  70. package/build/cli/commands/projection/kill/handler.js +16 -32
  71. package/build/cli/commands/projection/kill/meta.js +1 -1
  72. package/build/cli/commands/projection/revive/meta.js +4 -1
  73. package/build/cli/commands/projection/shared.js +28 -0
  74. package/build/cli/commands/projection/status/handler.js +1 -1
  75. package/build/cli/commands/projection/status/meta.js +1 -1
  76. package/build/cli/commands/projection/tell/handler.js +14 -30
  77. package/build/cli/commands/projection/tell/meta.js +4 -1
  78. package/build/cli/commands/projection/wait/handler.js +45 -22
  79. package/build/cli/commands/projection/wait/meta.js +8 -3
  80. package/build/cli/commands/registry.js +2 -54
  81. package/build/cli/commands/shared.js +10 -7
  82. package/build/cli/commands/skills/install/handler.js +2 -5
  83. package/build/cli/commands/skills/install/meta.js +1 -1
  84. package/build/cli/commands/skills/skills/handler.js +1 -1
  85. package/build/cli/commands/skills/skills/meta.js +1 -1
  86. package/build/cli/commands/system/completion/meta.js +1 -1
  87. package/build/cli/commands/system/dump-env/meta.js +1 -1
  88. package/build/cli/commands/system/guide/meta.js +1 -1
  89. package/build/cli/commands/task/add/handler.js +7 -15
  90. package/build/cli/commands/task/add/meta.js +4 -1
  91. package/build/cli/commands/task/catalog.js +2 -2
  92. package/build/cli/commands/task/doctor/meta.js +1 -1
  93. package/build/cli/commands/task/done/handler.js +4 -8
  94. package/build/cli/commands/task/done/meta.js +1 -1
  95. package/build/cli/commands/task/drop/handler.js +3 -6
  96. package/build/cli/commands/task/drop/meta.js +1 -1
  97. package/build/cli/commands/task/ls/handler.js +12 -8
  98. package/build/cli/commands/task/ls/meta.js +3 -3
  99. package/build/cli/commands/task/shared.js +14 -4
  100. package/build/cli/commands/task/{view → show}/handler.js +7 -8
  101. package/build/cli/commands/task/show/meta.js +8 -0
  102. package/build/cli/commands/task/start/handler.js +3 -6
  103. package/build/cli/commands/task/start/meta.js +1 -1
  104. package/build/cli/commands/task/stop/handler.js +4 -8
  105. package/build/cli/commands/task/stop/meta.js +1 -1
  106. package/build/cli/commands/task/task/handler.js +1 -1
  107. package/build/cli/commands/task/task/meta.js +1 -1
  108. package/build/cli/commands/task/update/handler.js +6 -11
  109. package/build/cli/commands/task/update/meta.js +4 -1
  110. package/build/cli/completion.js +1 -1
  111. package/build/cli/flags.js +37 -6
  112. package/build/cli/help.js +7 -14
  113. package/build/cli/index.js +112 -154
  114. package/build/cli/missing-subcommand.js +7 -0
  115. package/build/cli/output-stream-error-policy.js +19 -5
  116. package/build/cli/parse-flags.js +33 -7
  117. package/build/cli/parse-metadata.js +3 -9
  118. package/build/cli/parse-selectors.js +2 -1
  119. package/build/cli/parse.js +35 -5
  120. package/build/cli/projection-address.js +12 -12
  121. package/build/cli/render/address.js +41 -37
  122. package/build/cli/render/arc.js +24 -12
  123. package/build/cli/render/audit.js +121 -0
  124. package/build/cli/render/call.js +11 -5
  125. package/build/cli/render/errors.js +36 -1
  126. package/build/cli/render/format.js +4 -0
  127. package/build/cli/render/kanshi.js +6 -12
  128. package/build/cli/render/kill.js +9 -0
  129. package/build/cli/render/misc.js +58 -13
  130. package/build/cli/render/petition.js +20 -15
  131. package/build/cli/render/projection-activity.js +15 -6
  132. package/build/cli/render/shared.js +60 -21
  133. package/build/cli/render/skills.js +10 -0
  134. package/build/cli/render/status.js +56 -23
  135. package/build/cli/render/success-response.js +78 -0
  136. package/build/cli/render/task.js +66 -57
  137. package/build/cli/render/tell.js +5 -0
  138. package/build/cli/render/terminal-failure.js +63 -0
  139. package/build/cli/render/types.js +1 -0
  140. package/build/cli/render/wait.js +68 -38
  141. package/build/cli/types.js +1 -1
  142. package/build/config/akuma-loader.js +0 -5
  143. package/build/config/env-keys.js +3 -1
  144. package/build/config/env.js +3 -0
  145. package/build/config/provider-policy-ownership.js +3 -32
  146. package/build/config/settings/disease.js +8 -1
  147. package/build/config/settings/loader.js +11 -3
  148. package/build/config/settings/provider-instance-fields.js +51 -0
  149. package/build/config/settings/schema.js +6 -176
  150. package/build/core/addressing.js +66 -16
  151. package/build/core/amend.js +61 -18
  152. package/build/core/arc.js +33 -73
  153. package/build/core/audit.js +650 -0
  154. package/build/core/bind.js +82 -35
  155. package/build/core/call/call.js +15 -9
  156. package/build/core/call/context.js +6 -51
  157. package/build/core/call/execution.js +22 -22
  158. package/build/core/call/prompt.js +14 -1
  159. package/build/core/command-markdown.js +45 -0
  160. package/build/core/context.js +13 -7
  161. package/build/core/contract-view.js +22 -6
  162. package/build/core/draft.js +139 -19
  163. package/build/core/entry.js +3 -4
  164. package/build/core/forfeit.js +5 -2
  165. package/build/core/hints.js +1 -75
  166. package/build/core/identifier-slug.js +66 -0
  167. package/build/core/ids.js +25 -11
  168. package/build/core/ledger.js +20 -68
  169. package/build/core/markdown/lex.js +2 -1
  170. package/build/core/markdown/parser.js +0 -68
  171. package/build/core/markdown/render.js +68 -0
  172. package/build/core/markdown/sections.js +2 -1
  173. package/build/core/outcome-base.js +4 -0
  174. package/build/core/projection/generation/database.js +41 -52
  175. package/build/core/projection/generation/model.js +63 -2
  176. package/build/core/{projection-generation-continuation.js → projection/generation/projection-generation-continuation.js} +3 -3
  177. package/build/core/{projection-generation-execution.js → projection/generation/projection-generation-execution.js} +36 -53
  178. package/build/core/{projection-generation-identity.js → projection/generation/projection-generation-identity.js} +2 -2
  179. package/build/core/{projection-generation-launcher.js → projection/generation/projection-generation-launcher.js} +9 -7
  180. package/build/core/{projection-generation-process.js → projection/generation/projection-generation-process.js} +2 -1
  181. package/build/core/{projection-generation-runner.js → projection/generation/projection-generation-runner.js} +19 -3
  182. package/build/core/projection/generation/store.js +3 -3
  183. package/build/core/projection/generation/transitions.js +19 -3
  184. package/build/core/projection/index.js +27 -0
  185. package/build/core/{projection-activity.js → projection/projection-activity.js} +12 -0
  186. package/build/core/{projection-alias.js → projection/projection-alias.js} +13 -13
  187. package/build/core/projection/projection-coordinate.js +61 -0
  188. package/build/core/{projection-core.js → projection/projection-core.js} +9 -9
  189. package/build/core/{projection-execution-observer.js → projection/projection-execution-observer.js} +37 -78
  190. package/build/core/{projection-identity.js → projection/projection-identity.js} +1 -1
  191. package/build/core/{projection-kill.js → projection/projection-kill.js} +1 -1
  192. package/build/core/{projection-life-observer.js → projection/projection-life-observer.js} +16 -2
  193. package/build/core/{projection-mint.js → projection/projection-mint.js} +10 -10
  194. package/build/core/{projection-status.js → projection/projection-status.js} +163 -45
  195. package/build/core/projection/projection-terminal-failure.js +106 -0
  196. package/build/core/projection/projection-wait.js +275 -0
  197. package/build/core/projection/projection-wake.js +227 -0
  198. package/build/core/provider-loader-ports.js +4 -0
  199. package/build/core/ref-log.js +65 -40
  200. package/build/core/registry.js +63 -10
  201. package/build/core/renew-session.js +128 -0
  202. package/build/core/renew.js +448 -102
  203. package/build/core/scope.js +272 -68
  204. package/build/core/seal.js +165 -40
  205. package/build/core/{claim-delivery.js → settlement/claim-delivery.js} +8 -8
  206. package/build/core/{claim.js → settlement/claim.js} +32 -8
  207. package/build/core/settlement/index.js +19 -0
  208. package/build/core/{petition-claim-gates.js → settlement/petition-claim-gates.js} +24 -49
  209. package/build/core/{petition-forfeit.js → settlement/petition-forfeit.js} +2 -2
  210. package/build/core/{petition-head-guard.js → settlement/petition-head-guard.js} +2 -2
  211. package/build/core/settlement/petition-io.js +1 -0
  212. package/build/core/{petition-preview.js → settlement/petition-preview.js} +2 -2
  213. package/build/core/{petition.js → settlement/petition.js} +21 -12
  214. package/build/core/settlement/queue-read-model.js +143 -0
  215. package/build/core/settlement/queue-seat-allocation.js +156 -0
  216. package/build/core/settlement/queue.js +6 -0
  217. package/build/core/settlement/settlement.js +288 -0
  218. package/build/core/{verdict.js → settlement/verdict.js} +9 -37
  219. package/build/core/settlement/verification.js +59 -0
  220. package/build/core/status/board.js +62 -41
  221. package/build/core/status/ledger-batch.js +158 -0
  222. package/build/core/status/reconciliation.js +10 -4
  223. package/build/core/stored-agent-event.js +6 -2
  224. package/build/core/task/board.js +41 -4
  225. package/build/core/task/commands.js +45 -28
  226. package/build/core/task/document.js +5 -0
  227. package/build/core/task/index.js +12 -0
  228. package/build/core/task/model.js +8 -1
  229. package/build/core/task/query.js +172 -0
  230. package/build/core/task/settlement-git.js +265 -74
  231. package/build/core/task/settlement-policy.js +67 -29
  232. package/build/core/task/source-board.js +1 -0
  233. package/build/core/task/task-contract.js +444 -0
  234. package/build/core/{task-doctor.js → task/task-doctor.js} +1 -1
  235. package/build/core/{task-git-actor.js → task/task-git-actor.js} +5 -10
  236. package/build/core/{task-git-runtime.js → task/task-git-runtime.js} +3 -3
  237. package/build/core/{task-git-store.js → task/task-git-store.js} +90 -19
  238. package/build/core/task/task-repository.js +1 -0
  239. package/build/core/{task-store-repository.js → task/task-store-repository.js} +6 -4
  240. package/build/core/task/task-store.js +1 -0
  241. package/build/core/{task-worktree.js → task/task-worktree.js} +2 -1
  242. package/build/core/task/task.js +7 -0
  243. package/build/core/task/tree.js +37 -0
  244. package/build/core/task/validation.js +5 -2
  245. package/build/core/transcripts.js +15 -3
  246. package/build/core/worktree-path.js +43 -28
  247. package/build/flow-error.js +83 -39
  248. package/build/fs/path-coordinate.js +16 -0
  249. package/build/generated/version.js +3 -2
  250. package/build/git/branches.js +2 -1
  251. package/build/git/core.js +106 -2
  252. package/build/git/diff/parsers.js +23 -13
  253. package/build/git/diff/structured.js +127 -0
  254. package/build/git/path-coordinate.js +17 -0
  255. package/build/git/refs.js +57 -0
  256. package/build/git/streaming-batch.js +301 -0
  257. package/build/git/worktree.js +65 -0
  258. package/build/index.js +9 -9
  259. package/build/keiyaku.js +2 -36
  260. package/build/telemetry/logger.js +4 -2
  261. package/package.json +4 -10
  262. package/skills/keiyaku/SKILL.md +5 -4
  263. package/skills/keiyaku-akuma/SKILL.md +25 -11
  264. package/skills/keiyaku-task/SKILL.md +17 -8
  265. package/skills/keiyaku-workflow/SKILL.md +58 -9
  266. package/build/agents/providers/codex-sdk.js +0 -329
  267. package/build/cli/commands/task/view/meta.js +0 -8
  268. package/build/core/command-io.js +0 -174
  269. package/build/core/path-coordinate.js +0 -27
  270. package/build/core/petition-claim.js +0 -117
  271. package/build/core/petition-run.js +0 -83
  272. package/build/core/projection-coordinate.js +0 -63
  273. package/build/core/projection-wait.js +0 -165
  274. package/build/core/projection-wake.js +0 -136
  275. package/build/core/queue.js +0 -343
  276. package/build/core/task-contract.js +0 -237
  277. package/build/core/task.js +0 -4
  278. /package/build/{core/task-repository.js → agents/launch-snapshot/types.js} +0 -0
  279. /package/build/{core/task-store.js → agents/providers/claude-agent-sdk/terms.js} +0 -0
  280. /package/build/core/{projection-generation-runtime.js → projection/generation/projection-generation-runtime.js} +0 -0
  281. /package/build/core/{projection-life-protocol.js → projection/projection-life-protocol.js} +0 -0
  282. /package/build/core/{projection-runner-lock.js → projection/projection-runner-lock.js} +0 -0
@@ -0,0 +1,288 @@
1
+ import { FlowError } from "../../flow-error.js";
2
+ import { readCurrentContractContext } from "../context.js";
3
+ import { readLedger } from "../ledger.js";
4
+ import { commissionTarget } from "../status/drift.js";
5
+ import { deriveContractState } from "../status/lifecycle.js";
6
+ import { getCurrentBranch, requireGitRepository } from "../../git/branches.js";
7
+ import { readContractLog } from "../log.js";
8
+ import { listRefLogCommits } from "../ref-log.js";
9
+ import { effectiveScope } from "../seal.js";
10
+ import { scopePathEvidence } from "../scope.js";
11
+ import { nextReviewResponseNumber } from "../transcripts.js";
12
+ import { claimContract } from "./claim.js";
13
+ import { buildClaimGatePipeline, } from "./petition-claim-gates.js";
14
+ import { resolveVerificationCommands } from "./verification.js";
15
+ import { forfeitKeiyaku } from "./petition-forfeit.js";
16
+ import { assertHeadUnchanged } from "./petition-head-guard.js";
17
+ import { petitionContract } from "./petition.js";
18
+ import { verdictContract } from "./verdict.js";
19
+ const VERDICT_DENIED_CODE = "VERDICT_DENIED";
20
+ async function resolveClaimGateConfig(cwd) {
21
+ const merged = {
22
+ verificationCommands: [],
23
+ };
24
+ try {
25
+ merged.verificationCommands = await resolveVerificationCommands(cwd);
26
+ return merged;
27
+ }
28
+ catch (error) {
29
+ if (error instanceof FlowError && error.code === "INVALID_SETTINGS") {
30
+ throw new FlowError(VERDICT_DENIED_CODE, `Configuration Error: ${error.message}`, {
31
+ cause: error,
32
+ facts: error.facts,
33
+ });
34
+ }
35
+ throw error;
36
+ }
37
+ }
38
+ async function loadCloseContext(cwd, requestedContractId) {
39
+ await requireGitRepository(cwd, "petition");
40
+ if (requestedContractId) {
41
+ const ledger = await readLedger(cwd, requestedContractId);
42
+ if (!ledger) {
43
+ throw new FlowError("INTERNAL_STATE", `contract ${requestedContractId} not found or not active`);
44
+ }
45
+ const target = commissionTarget(ledger.entries);
46
+ if (!target) {
47
+ throw new FlowError("INTERNAL_STATE", `contract ${requestedContractId} active ledger is missing commission target`);
48
+ }
49
+ return {
50
+ cwd,
51
+ keiyakuBranch: `keiyaku/${requestedContractId}`,
52
+ baseBranch: target.branch,
53
+ contractId: requestedContractId,
54
+ };
55
+ }
56
+ let contractContext;
57
+ try {
58
+ contractContext = await readCurrentContractContext(cwd);
59
+ }
60
+ catch (error) {
61
+ if (!(error instanceof FlowError) || error.code !== "EMPTY_PARAM") {
62
+ throw error;
63
+ }
64
+ throw new FlowError("NOT_ACTIVE_KEIYAKU_BRANCH", "Current workspace is not addressing an active Keiyaku contract.");
65
+ }
66
+ const keiyakuBranch = await getCurrentBranch(cwd);
67
+ return {
68
+ cwd,
69
+ keiyakuBranch,
70
+ baseBranch: contractContext.baseBranch,
71
+ contractId: contractContext.contractId,
72
+ };
73
+ }
74
+ async function throwClaimGateFailure(failure, _input, context, _dependencies) {
75
+ void _input;
76
+ void _dependencies;
77
+ void context.contractId;
78
+ if (failure.reviewHeadBeforeWrite) {
79
+ await assertHeadUnchanged(context.cwd, failure.reviewHeadBeforeWrite, "claim review");
80
+ }
81
+ throw new FlowError(failure.code, failure.message, {
82
+ review: failure.review,
83
+ facts: failure.facts,
84
+ });
85
+ }
86
+ async function runClaimGatePipelineOrThrow(input, context, dependencies) {
87
+ const { verificationCommands } = await dependencies.resolveClaimGateConfig(context.cwd);
88
+ let review;
89
+ let reviewHeadBeforeWrite;
90
+ let reviewerActor;
91
+ for (const gate of buildClaimGatePipeline()) {
92
+ const result = await gate.evaluate({
93
+ cwd: context.cwd,
94
+ contractId: context.contractId,
95
+ input,
96
+ verificationCommands,
97
+ baseBranch: dependencies.baseBranch,
98
+ reviewNumber: dependencies.reviewNumber,
99
+ });
100
+ if (result.review) {
101
+ review = result.review;
102
+ reviewHeadBeforeWrite = result.reviewHeadBeforeWrite;
103
+ }
104
+ if (result.reviewerActor)
105
+ reviewerActor = result.reviewerActor;
106
+ if (!result.pass) {
107
+ await throwClaimGateFailure(result, input, context, dependencies);
108
+ }
109
+ }
110
+ return { review, reviewHeadBeforeWrite, reviewerActor };
111
+ }
112
+ function maxArc(entries) {
113
+ return entries.filter((entry) => entry.kind === "arc").length;
114
+ }
115
+ function cleanupWarnings(cleanup) {
116
+ return [
117
+ ...(cleanup.worktreeRemoveFailure
118
+ ? [`Could not remove contract worktree ${cleanup.worktreeRemoveFailure.path}: ${cleanup.worktreeRemoveFailure.reason}`]
119
+ : []),
120
+ ...(cleanup.branchDeleteFailure
121
+ ? [`Could not delete contract branch ${cleanup.branchDeleteFailure.name}: ${cleanup.branchDeleteFailure.reason}`]
122
+ : []),
123
+ ];
124
+ }
125
+ function findClaimEntry(entries) {
126
+ return entries.find((entry) => entry.kind === "claim");
127
+ }
128
+ /** Verdict for the open petition window (after last petition, before claim). */
129
+ function latestCurrentPetitionVerdict(entries) {
130
+ for (let index = entries.length - 1; index >= 0; index -= 1) {
131
+ const entry = entries[index];
132
+ if (entry.kind === "verdict")
133
+ return entry;
134
+ if (entry.kind === "petition")
135
+ return undefined;
136
+ }
137
+ return undefined;
138
+ }
139
+ /**
140
+ * Crash-recovery head: petition → approved verdict → no claim.
141
+ * Orchestrator resumes claim delivery only; no gates, petition, or re-judgment.
142
+ */
143
+ function isApprovedVerdictAwaitingClaim(entries) {
144
+ const state = deriveContractState(entries);
145
+ if (state.state !== "petitioned")
146
+ return false;
147
+ if (findClaimEntry(entries))
148
+ return false;
149
+ return latestCurrentPetitionVerdict(entries)?.data.result === "approved";
150
+ }
151
+ async function recoverCurrentPetitionSeal(cwd, ledger) {
152
+ let petitionIndex = -1;
153
+ for (let index = ledger.entries.length - 1; index >= 0; index -= 1) {
154
+ if (ledger.entries[index]?.kind === "petition") {
155
+ petitionIndex = index;
156
+ break;
157
+ }
158
+ }
159
+ if (petitionIndex <= 0)
160
+ return undefined;
161
+ const seal = ledger.entries[petitionIndex - 1];
162
+ if (seal.kind !== "seal" || seal.data.sealedBy !== "petition")
163
+ return undefined;
164
+ const commits = await listRefLogCommits(cwd, ledger.head, ledger.ref);
165
+ const ledgerHead = commits[petitionIndex - 1];
166
+ if (!ledgerHead || commits.length !== ledger.entries.length) {
167
+ throw new FlowError("INTERNAL_STATE", `contract ${ledger.contractId} ledger entries do not align with ref-log commits during petition recovery`);
168
+ }
169
+ const scopeViolationEvidence = await scopePathEvidence(cwd, effectiveScope(ledger.entries), seal.data.scopeAudit.violations);
170
+ return {
171
+ ...seal.data,
172
+ scopeViolationEvidence,
173
+ ledgerHead,
174
+ };
175
+ }
176
+ async function finishApprovedClaim(context, options) {
177
+ const { cwd, baseBranch, contractId } = context;
178
+ // Capture arc facts before claim cleanup may remove the delivery worktree cwd.
179
+ const log = await readContractLog({ cwd, contractId });
180
+ const claimed = await claimContract({
181
+ cwd,
182
+ contractId,
183
+ actor: "keiyaku",
184
+ nowMs: options.nowMs,
185
+ });
186
+ const cleanup = claimed.cleanup ?? { absent: ["worktree", "branch"] };
187
+ const warnings = [...(claimed.warnings ?? []), ...cleanupWarnings(cleanup)];
188
+ return {
189
+ status: "success",
190
+ result: "merged",
191
+ arc: maxArc(log.entries),
192
+ currentBranch: baseBranch,
193
+ baseBranch,
194
+ commit: claimed.mergeCommit,
195
+ mergedInto: baseBranch,
196
+ ...(cleanup.deletedBranch ? { deletedBranch: cleanup.deletedBranch } : {}),
197
+ cleanup,
198
+ diff: `Merged contract ${contractId} with merge-tree/commit-tree CAS.`,
199
+ // Prior public outcome carried sealed facts when petition sealed an open arc.
200
+ ...(options.sealed ? { sealed: options.sealed } : {}),
201
+ ...(warnings.length > 0 ? { meta: { warnings } } : {}),
202
+ };
203
+ }
204
+ /**
205
+ * Settlement claim path: gates → petition → pure verdict → claim delivery.
206
+ * Stage modules do not call each other; this orchestrator owns the sequence.
207
+ *
208
+ * Atomicity equivalence: previously verdictContract appended the verdict entry
209
+ * then immediately invoked claimContract. That remains two sequential append
210
+ * windows (verdict then claim). Crash after approved verdict leaves state
211
+ * petitioned with an approved verdict; the orchestrator detects that head and
212
+ * resumes claimContract idempotently without re-running gates or judgment.
213
+ */
214
+ export async function claimKeiyaku(input, context, dependencies) {
215
+ const { cwd, baseBranch, contractId } = context;
216
+ // Crash recovery: petition → approved verdict → no claim.
217
+ // Resume claim delivery only; do not re-gate, re-petition, or re-judge.
218
+ const existing = await readLedger(cwd, contractId);
219
+ if (existing && isApprovedVerdictAwaitingClaim(existing.entries)) {
220
+ const sealed = await recoverCurrentPetitionSeal(cwd, existing);
221
+ return finishApprovedClaim(context, {
222
+ nowMs: Date.now(),
223
+ ...(sealed ? { sealed } : {}),
224
+ });
225
+ }
226
+ const claimResult = "dangerouslyBypassGates" in input && input.dangerouslyBypassGates
227
+ ? { review: undefined, reviewHeadBeforeWrite: undefined, bypassedByArchitect: true }
228
+ : {
229
+ ...(await runClaimGatePipelineOrThrow(input, { cwd, contractId }, {
230
+ ...dependencies,
231
+ baseBranch,
232
+ reviewNumber: await nextReviewResponseNumber(cwd),
233
+ })),
234
+ bypassedByArchitect: false,
235
+ };
236
+ if (claimResult.review && claimResult.reviewHeadBeforeWrite) {
237
+ await assertHeadUnchanged(cwd, claimResult.reviewHeadBeforeWrite, "claim review");
238
+ }
239
+ const oath = String(input.oath ?? "").trim();
240
+ if (!oath) {
241
+ throw new FlowError("OATH_MISSING", "Oath missing.");
242
+ }
243
+ // 1. Petition: seal + admit onto the target-local settlement queue.
244
+ const petition = await petitionContract({
245
+ cwd,
246
+ contractId,
247
+ actor: "keiyaku",
248
+ nowMs: Date.now(),
249
+ intent: "claim",
250
+ oath,
251
+ appendScope: input.appendScope,
252
+ });
253
+ // 2. Pure verdict: judgment facts only; no claim execution.
254
+ // Capture one clock for verdict + claim so ledger timestamps match the prior
255
+ // verdict→claim atomic pair (verdict previously forwarded its own nowMs).
256
+ const judgmentNowMs = Date.now();
257
+ const verdict = await verdictContract({
258
+ cwd,
259
+ contractId,
260
+ actor: claimResult.reviewerActor ?? "keiyaku",
261
+ nowMs: judgmentNowMs,
262
+ result: "approved",
263
+ summary: claimResult.review?.findings?.trim() || "礼成",
264
+ });
265
+ // 3. Execution: approved → claim delivery (same sequential append window as before).
266
+ // Rejected verdict publication is verdictContract itself (state returns active).
267
+ if (verdict.result !== "approved") {
268
+ throw new FlowError("INTERNAL_STATE", `settlement expected approved verdict for ${contractId}, got ${verdict.result}`);
269
+ }
270
+ return finishApprovedClaim(context, {
271
+ nowMs: judgmentNowMs,
272
+ ...(petition.sealed ? { sealed: petition.sealed } : {}),
273
+ });
274
+ }
275
+ /** Public settlement entrypoint for CLI petition (claim or forfeit). */
276
+ export async function petitionKeiyaku(input) {
277
+ const context = await loadCloseContext(input.cwd, input.contractId);
278
+ const { intent } = input;
279
+ if (intent === "FORFEIT") {
280
+ return forfeitKeiyaku(context);
281
+ }
282
+ if (intent === "CLAIM") {
283
+ return claimKeiyaku(input, context, {
284
+ resolveClaimGateConfig,
285
+ });
286
+ }
287
+ throw new FlowError("INVALID_CLOSE_INTENT", `unsupported close intent: ${intent}`);
288
+ }
@@ -1,8 +1,7 @@
1
- import { FlowError } from "../flow-error.js";
2
- import { claimContract } from "./claim.js";
3
- import { activeLedgerRef, appendWithRetry, readLedger } from "./ledger.js";
4
- import { assertClaimQueueHead, queueMembership, readQueueReadModel } from "./queue.js";
5
- import { deriveContractState } from "./status/lifecycle.js";
1
+ import { FlowError } from "../../flow-error.js";
2
+ import { activeLedgerRef, appendWithRetry, readLedger } from "../ledger.js";
3
+ import { assertClaimQueueHead, readQueueReadModel } from "./queue.js";
4
+ import { deriveContractState } from "../status/lifecycle.js";
6
5
  function latestVerdict(entries) {
7
6
  for (let index = entries.length - 1; index >= 0; index -= 1) {
8
7
  const entry = entries[index];
@@ -19,6 +18,10 @@ function assertPetitioned(contractId, entries) {
19
18
  throw new FlowError("INTERNAL_STATE", `contract ${contractId} cannot verdict from state ${state.state}`);
20
19
  }
21
20
  }
21
+ /**
22
+ * Append a verdict entry and return typed judgment facts.
23
+ * Does not claim, clean up, or otherwise execute delivery.
24
+ */
22
25
  export async function verdictContract(input) {
23
26
  const at = new Date(input.nowMs).toISOString();
24
27
  const current = await readLedger(input.cwd, input.contractId);
@@ -69,40 +72,9 @@ export async function verdictContract(input) {
69
72
  if (verdict.data.result !== input.result) {
70
73
  throw new FlowError("INTERNAL_STATE", `contract ${input.contractId} already has ${verdict.data.result} verdict`);
71
74
  }
72
- if (verdict.data.result === "rejected") {
73
- // Rejected verdict naturally returns state to active and clears membership.
74
- return {
75
- contractId: input.contractId,
76
- result: "rejected",
77
- summary: verdict.data.summary,
78
- queuePosition: null,
79
- };
80
- }
81
- const claimOnApproval = input.claimOnApproval ?? true;
82
- if (claimOnApproval) {
83
- const claimed = await claimContract({
84
- cwd: input.cwd,
85
- contractId: input.contractId,
86
- actor: "keiyaku",
87
- nowMs: input.nowMs,
88
- mergeDelivery: input.mergeDelivery,
89
- afterMerge: input.afterMerge,
90
- });
91
- return {
92
- contractId: input.contractId,
93
- result: "approved",
94
- summary: verdict.data.summary,
95
- claimed,
96
- queuePosition: null,
97
- };
98
- }
99
- // claimOnApproval false remains petitioned at target-local position 1.
100
- const model = await readQueueReadModel(input.cwd);
101
- const membership = queueMembership(model, input.contractId);
102
75
  return {
103
76
  contractId: input.contractId,
104
- result: "approved",
77
+ result: verdict.data.result,
105
78
  summary: verdict.data.summary,
106
- queuePosition: membership?.position ?? 1,
107
79
  };
108
80
  }
@@ -0,0 +1,59 @@
1
+ import { spawn } from "node:child_process";
2
+ import { getConfig } from "../../config/env.js";
3
+ import { assertSettingsKnobUsable } from "../../config/settings/disease.js";
4
+ import { loadKeiyakuSettings } from "../../config/settings/loader.js";
5
+ import { FlowError } from "../../flow-error.js";
6
+ const AUDIT_OUTPUT_TAIL_BYTES = 8_192;
7
+ function tail(value, limit = AUDIT_OUTPUT_TAIL_BYTES) {
8
+ if (value.byteLength <= limit) {
9
+ return { tail: value.toString("utf8"), truncated: false, omittedBytes: 0 };
10
+ }
11
+ return {
12
+ tail: value.subarray(value.byteLength - limit).toString("utf8"),
13
+ truncated: true,
14
+ omittedBytes: value.byteLength - limit,
15
+ };
16
+ }
17
+ export function verificationExecutor(command) {
18
+ const basename = command.command.split(/[\\/]/).pop() ?? "";
19
+ const executor = basename.toLowerCase().replace(/\.exe$/, "");
20
+ if (executor !== "bash" && executor !== "zsh" && executor !== "pwsh") {
21
+ return { cause: "unsupported_executor" };
22
+ }
23
+ const bodyIndex = command.args.findIndex((arg) => arg === "-c" || arg === "-lc" || arg === "-Command");
24
+ const body = bodyIndex < 0 ? undefined : command.args[bodyIndex + 1];
25
+ if (body === undefined)
26
+ return { cause: "missing_command_body" };
27
+ return { executor, body };
28
+ }
29
+ export async function resolveVerificationCommands(cwd) {
30
+ const loaded = await loadKeiyakuSettings(cwd);
31
+ assertSettingsKnobUsable(loaded, "gate");
32
+ return loaded.knobs?.gate?.commands ?? [];
33
+ }
34
+ export async function runVerificationCommand(cwd, verification) {
35
+ const started = process.hrtime.bigint();
36
+ return await new Promise((resolve, reject) => {
37
+ const child = spawn(verification.command, verification.args, {
38
+ cwd,
39
+ stdio: ["ignore", "pipe", "pipe"],
40
+ env: getConfig().runtimeEnv,
41
+ });
42
+ const stdout = [];
43
+ const stderr = [];
44
+ child.stdout.on("data", (chunk) => stdout.push(Buffer.from(chunk)));
45
+ child.stderr.on("data", (chunk) => stderr.push(Buffer.from(chunk)));
46
+ child.on("error", reject);
47
+ child.on("close", (code) => {
48
+ const durationMs = Number((process.hrtime.bigint() - started) / 1000000n);
49
+ resolve({
50
+ code: typeof code === "number" ? code : -1,
51
+ durationMs,
52
+ stdout: tail(Buffer.concat(stdout)),
53
+ stderr: tail(Buffer.concat(stderr)),
54
+ });
55
+ });
56
+ }).catch((error) => {
57
+ throw new FlowError("CLOSE_QUALITY_GATE_FAILED", `verification '${verification.label}' could not start`, { cause: error });
58
+ });
59
+ }
@@ -1,38 +1,64 @@
1
1
  import { loadKeiyakuSettings } from "../../config/settings/loader.js";
2
2
  import { observeGitRepository } from "../../git/branches.js";
3
- import { listContractIds, readLedger } from "../ledger.js";
4
- import { readProjectionStatusBoard } from "../projection-status.js";
5
- import { deriveQueueReadModel, queueMembership, } from "../queue.js";
3
+ import { readProjectionStatusBoard } from "../projection/index.js";
4
+ import { deriveQueueReadModel, queueMembership, } from "../settlement/index.js";
6
5
  import { findOpenArc, getMaxArc, receiptsAfterFence } from "../seal.js";
7
- import { readTaskBoard } from "../task.js";
8
- import { openTaskStore } from "../task-git-store.js";
9
- import { taskRepositoryFromStore } from "../task-store-repository.js";
6
+ import { openTaskStore, readTaskBoard, taskRepositoryFromStore, taskUpdatedAt, } from "../task/index.js";
10
7
  import { deriveBootstrapDiagnostics, deriveContractState, hasCurrentPetitionVerdict, isTerminalState, } from "./lifecycle.js";
11
8
  import { commissionTarget, evaluateBaseDrift, latestBase, resolveDefaultBranchFromSettings, } from "./drift.js";
12
9
  import { computeClaimReconciliation, } from "./reconciliation.js";
10
+ import { readStatusLedgers } from "./ledger-batch.js";
13
11
  function compareTaskStatusRows(left, right) {
14
- if (left.pri !== right.pri)
15
- return left.pri - right.pri;
16
- if (left.createdAt !== right.createdAt)
17
- return left.createdAt < right.createdAt ? -1 : 1;
12
+ const leftUpdated = taskUpdatedAt(left);
13
+ const rightUpdated = taskUpdatedAt(right);
14
+ if (leftUpdated !== rightUpdated)
15
+ return leftUpdated > rightUpdated ? -1 : 1;
18
16
  return left.id < right.id ? -1 : left.id > right.id ? 1 : 0;
19
17
  }
20
- async function readTaskStatusSummary(cwd) {
18
+ function timestampAge(timestamp, now) {
19
+ const value = Date.parse(timestamp);
20
+ return Number.isFinite(value) ? Math.max(0, now - value) : 0;
21
+ }
22
+ export function deriveTaskStatusSummary(board, now) {
23
+ const eligible = board.tasks.filter((task) => !task.contractId && (task.state === "open" || task.state === "in_progress"));
24
+ const inProgress = eligible.filter((task) => task.state === "in_progress").length;
25
+ const topTasks = [
26
+ ...eligible.filter((task) => task.state === "in_progress").sort(compareTaskStatusRows),
27
+ ...eligible.filter((task) => task.state === "open" && !task.blocked).sort(compareTaskStatusRows),
28
+ ]
29
+ .slice(0, 5)
30
+ .map((task) => {
31
+ const updatedAt = taskUpdatedAt(task);
32
+ return {
33
+ id: task.id,
34
+ title: task.title,
35
+ pri: task.pri,
36
+ state: task.state,
37
+ blocked: task.blocked,
38
+ createdAt: task.createdAt,
39
+ updatedAt,
40
+ ageMs: timestampAge(task.createdAt, now),
41
+ activityAgeMs: timestampAge(updatedAt, now),
42
+ };
43
+ });
44
+ const ready = eligible.filter((task) => task.state === "open" && !task.blocked).length;
45
+ const blocked = eligible.filter((task) => task.state === "open" && task.blocked).length;
46
+ if (board.tasks.length === 0 && board.diseases.length === 0)
47
+ return undefined;
48
+ return {
49
+ total: board.tasks.length,
50
+ inProgress,
51
+ ready,
52
+ blocked,
53
+ omitted: inProgress + ready - topTasks.length,
54
+ topTasks,
55
+ diseases: board.diseases,
56
+ };
57
+ }
58
+ async function readTaskStatusSummary(cwd, now) {
21
59
  try {
22
60
  const board = await readTaskBoard(taskRepositoryFromStore(await openTaskStore(cwd)));
23
- const eligible = board.tasks.filter((task) => !task.contractId && (task.state === "open" || task.state === "in_progress"));
24
- const inProgress = eligible.filter((task) => task.state === "in_progress").length;
25
- const topTasks = [
26
- ...eligible.filter((task) => task.state === "in_progress").sort(compareTaskStatusRows),
27
- ...eligible.filter((task) => task.state === "open" && !task.blocked).sort(compareTaskStatusRows),
28
- ]
29
- .slice(0, 5)
30
- .map(({ id, title, pri, createdAt, state, blocked }) => ({ id, title, pri, createdAt, state, blocked }));
31
- const ready = eligible.filter((task) => task.state === "open" && !task.blocked).length;
32
- const blocked = eligible.filter((task) => task.state === "open" && task.blocked).length;
33
- if (board.tasks.length === 0 && board.diseases.length === 0)
34
- return undefined;
35
- return { total: board.tasks.length, inProgress, ready, blocked, topTasks, diseases: board.diseases };
61
+ return deriveTaskStatusSummary(board, now);
36
62
  }
37
63
  catch (error) {
38
64
  return {
@@ -40,6 +66,7 @@ async function readTaskStatusSummary(cwd) {
40
66
  inProgress: 0,
41
67
  ready: 0,
42
68
  blocked: 0,
69
+ omitted: 0,
43
70
  topTasks: [],
44
71
  diseases: [],
45
72
  diagnostic: error instanceof Error ? error.message : String(error),
@@ -141,14 +168,14 @@ function deriveLastActiveAge(entries, now) {
141
168
  export async function readKanshiBoard(cwd, now, opts) {
142
169
  const targetFilter = opts?.target ?? null;
143
170
  const loadedSettings = await loadKeiyakuSettings(cwd);
144
- const tasks = await readTaskStatusSummary(cwd);
171
+ const tasks = await readTaskStatusSummary(cwd, now);
145
172
  const repository = await observeGitRepository(cwd);
146
173
  if (repository.capability === "unknown")
147
174
  throw repository.error;
148
175
  if (repository.capability !== "available" || repository.coordinate !== "repository") {
149
176
  return {
150
177
  contracts: [],
151
- projections: readProjectionStatusBoard(cwd, now, { showAll: opts?.showAllProjections }),
178
+ projections: await readProjectionStatusBoard(cwd, now, { showAll: opts?.showAllProjections }),
152
179
  queueLength: 0,
153
180
  diseases: loadedSettings.diseases,
154
181
  claimReconciliation: null,
@@ -165,22 +192,14 @@ export async function readKanshiBoard(cwd, now, opts) {
165
192
  catch {
166
193
  defaultBranch = null;
167
194
  }
168
- const contractIds = await listContractIds(cwd);
169
195
  const terminalById = new Map();
170
- const ledgers = [];
196
+ const ledgers = await readStatusLedgers(cwd);
171
197
  const readableLedgers = [];
172
- for (const id of contractIds) {
173
- try {
174
- const ledger = await readLedger(cwd, id);
175
- if (ledger) {
176
- ledgers.push(ledger);
177
- readableLedgers.push(ledger);
178
- terminalById.set(id, isTerminalState(deriveContractState(ledger.entries)));
179
- }
180
- }
181
- catch (error) {
182
- ledgers.push({ id, error: error instanceof Error ? error : new Error(String(error)) });
183
- }
198
+ for (const ledger of ledgers) {
199
+ if ("error" in ledger)
200
+ continue;
201
+ readableLedgers.push(ledger);
202
+ terminalById.set(ledger.contractId, isTerminalState(deriveContractState(ledger.entries)));
184
203
  }
185
204
  const queueModel = deriveQueueReadModel(readableLedgers);
186
205
  const boardQueueLength = queueModel.targetQueues.reduce((sum, queue) => sum + queue.ordered.length, 0);
@@ -194,6 +213,7 @@ export async function readKanshiBoard(cwd, now, opts) {
194
213
  contracts.push({
195
214
  id: item.id,
196
215
  slug: contractSlugFromId(item.id),
216
+ title: contractSlugFromId(item.id),
197
217
  state: { state: "corrupt", at: 0, reason: item.error.message },
198
218
  queueHeadStalled: false,
199
219
  arcs: 0,
@@ -245,6 +265,7 @@ export async function readKanshiBoard(cwd, now, opts) {
245
265
  contracts.push({
246
266
  id: item.contractId,
247
267
  slug: contractSlugFromId(item.contractId),
268
+ title: bind?.data.name ?? contractSlugFromId(item.contractId),
248
269
  state,
249
270
  queuePosition,
250
271
  queueHeadStalled: isQueueHeadStall({
@@ -277,7 +298,7 @@ export async function readKanshiBoard(cwd, now, opts) {
277
298
  const claimReconciliation = await computeClaimReconciliation(cwd, defaultBranch);
278
299
  return {
279
300
  contracts: filteredContracts,
280
- projections: readProjectionStatusBoard(cwd, now, { showAll: opts?.showAllProjections }),
301
+ projections: await readProjectionStatusBoard(cwd, now, { showAll: opts?.showAllProjections }),
281
302
  queueLength,
282
303
  defaultBranch: defaultBranch?.branch,
283
304
  defaultHead: defaultBranch?.head,