@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,8 +1,3 @@
1
- import { spawn } from "node:child_process";
2
- import * as fs from "node:fs/promises";
3
- import * as os from "node:os";
4
- import * as path from "node:path";
5
- import { createGit, wrapGitError } from "../git/core.js";
6
1
  export class ScopePatternError extends Error {
7
2
  code = "INVALID_SCOPE_PATTERN";
8
3
  constructor(message) {
@@ -41,9 +36,9 @@ export function isBlankScopePatternLine(line) {
41
36
  return canonicalizeScopePatternInput(line).length === 0;
42
37
  }
43
38
  /**
44
- * Canonicalize a scope gitignore pattern for exclude matching and persistence.
39
+ * Canonicalize a scope Gitignore-like pattern for matching and persistence.
45
40
  * Strips only a cosmetic leading `./` (including after `!`); preserves meaningful
46
- * leading `/` anchoring, gitignore backslash escapes, and escaped trailing spaces.
41
+ * leading `/` anchoring, supported backslash escapes, and escaped trailing spaces.
47
42
  */
48
43
  export function normalizeScopePattern(pattern) {
49
44
  if (pattern.includes("\0") || pattern.includes("\n") || pattern.includes("\r")) {
@@ -78,75 +73,157 @@ export function normalizeScopePath(filePath) {
78
73
  throw new ScopePatternError("scope path cannot contain NUL");
79
74
  return normalized;
80
75
  }
81
- function runCheckIgnore(gitDir, workTree, cwd, paths) {
82
- return new Promise((resolve, reject) => {
83
- const child = spawn("git", [
84
- `--git-dir=${gitDir}`,
85
- `--work-tree=${workTree}`,
86
- "check-ignore",
87
- "--no-index",
88
- "-z",
89
- "--stdin",
90
- ], {
91
- cwd,
92
- env: {
93
- ...process.env,
94
- GIT_TERMINAL_PROMPT: "0",
95
- GCM_INTERACTIVE: "Never",
96
- GIT_CONFIG_GLOBAL: "/dev/null",
97
- GIT_CONFIG_NOSYSTEM: "1",
98
- LC_ALL: "C",
99
- },
100
- stdio: ["pipe", "pipe", "pipe"],
101
- });
102
- const stdout = [];
103
- const stderr = [];
104
- child.stdout.on("data", (chunk) => stdout.push(chunk));
105
- child.stderr.on("data", (chunk) => stderr.push(chunk));
106
- child.stdin.on("error", reject);
107
- child.on("error", reject);
108
- child.on("close", (code) => {
109
- if (code !== 0 && code !== 1) {
110
- reject(wrapGitError("check-ignore --no-index -z --stdin", {
111
- message: `git check-ignore exited with status ${code ?? "unknown"}`,
112
- stderr: Buffer.concat(stderr).toString("utf8"),
113
- stdout: Buffer.concat(stdout).toString("utf8"),
114
- }, cwd));
115
- return;
76
+ function splitPatternComponents(pattern) {
77
+ const components = [];
78
+ let component = "";
79
+ let escaped = false;
80
+ for (const character of pattern) {
81
+ if (escaped) {
82
+ component += `\\${character}`;
83
+ escaped = false;
84
+ }
85
+ else if (character === "\\") {
86
+ escaped = true;
87
+ }
88
+ else if (character === "/") {
89
+ components.push(component);
90
+ component = "";
91
+ }
92
+ else {
93
+ component += character;
94
+ }
95
+ }
96
+ if (escaped)
97
+ component += "\\\\";
98
+ components.push(component);
99
+ return components;
100
+ }
101
+ function parseScopePattern(pattern) {
102
+ const normalized = normalizeScopePattern(pattern);
103
+ const escapedLeading = normalized.startsWith("\\!") || normalized.startsWith("\\#");
104
+ const negative = normalized.startsWith("!");
105
+ const body = negative ? normalized.slice(1) : normalized;
106
+ const comment = !escapedLeading && body.startsWith("#");
107
+ const anchored = body.startsWith("/");
108
+ const unanchored = anchored ? body.slice(1) : body;
109
+ const directory = unanchored.endsWith("/");
110
+ const source = directory ? unanchored.slice(0, -1) : unanchored;
111
+ const segments = splitPatternComponents(source);
112
+ return { negative, comment, directory, segments, componentOnly: !anchored && !source.includes("/") };
113
+ }
114
+ function componentMatches(pattern, value) {
115
+ let expression = "^";
116
+ for (let index = 0; index < pattern.length; index += 1) {
117
+ const character = pattern[index];
118
+ if (character === "\\") {
119
+ index += 1;
120
+ expression += escapeRegex(pattern[index] ?? "\\");
121
+ }
122
+ else if (character === "*") {
123
+ if (pattern[index + 1] === "*") {
124
+ expression += ".*";
125
+ index += 1;
126
+ }
127
+ else
128
+ expression += "[^/]*";
129
+ }
130
+ else if (character === "?")
131
+ expression += "[^/]";
132
+ else if (character === "[") {
133
+ const close = pattern.indexOf("]", index + 1);
134
+ if (close === -1)
135
+ expression += "\\[";
136
+ else {
137
+ const rawClass = pattern.slice(index + 1, close);
138
+ const classBody = rawClass.startsWith("!") ? `^${rawClass.slice(1)}` : rawClass;
139
+ expression += `[${classBody.replace(/\\/g, "\\\\")}]`;
140
+ index = close;
141
+ }
142
+ }
143
+ else
144
+ expression += escapeRegex(character);
145
+ }
146
+ return new RegExp(`${expression}$`).test(value);
147
+ }
148
+ function escapeRegex(value) {
149
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
150
+ }
151
+ function patternMatchesPath(parsed, filePath) {
152
+ if (parsed.comment)
153
+ return false;
154
+ const pathSegments = filePath.split("/");
155
+ if (parsed.componentOnly) {
156
+ const lastMatchIndex = parsed.directory ? pathSegments.length - 1 : pathSegments.length;
157
+ return pathSegments.slice(0, lastMatchIndex).some((segment) => componentMatches(parsed.segments[0], segment));
158
+ }
159
+ const memo = new Map();
160
+ const matchesFrom = (patternIndex, pathIndex) => {
161
+ const key = `${patternIndex}:${pathIndex}`;
162
+ const cached = memo.get(key);
163
+ if (cached !== undefined)
164
+ return cached;
165
+ let matched;
166
+ if (patternIndex === parsed.segments.length) {
167
+ matched = parsed.directory ? pathIndex < pathSegments.length : pathIndex === pathSegments.length;
168
+ memo.set(key, matched);
169
+ return matched;
170
+ }
171
+ const segment = parsed.segments[patternIndex];
172
+ if (segment === "**") {
173
+ matched = false;
174
+ for (let nextPathIndex = pathIndex; nextPathIndex <= pathSegments.length; nextPathIndex += 1) {
175
+ if (matchesFrom(patternIndex + 1, nextPathIndex)) {
176
+ matched = true;
177
+ break;
178
+ }
116
179
  }
117
- resolve(new Set(Buffer.concat(stdout)
118
- .toString("utf8")
119
- .split("\0")
120
- .filter(Boolean)));
180
+ }
181
+ else {
182
+ matched = pathIndex < pathSegments.length
183
+ && componentMatches(segment, pathSegments[pathIndex])
184
+ && matchesFrom(patternIndex + 1, pathIndex + 1);
185
+ }
186
+ memo.set(key, matched);
187
+ return matched;
188
+ };
189
+ return matchesFrom(0, 0);
190
+ }
191
+ /**
192
+ * Evaluate the settled ordered Gitignore-like scope subset without invoking Git.
193
+ */
194
+ export async function scopePathEvidence(_cwd, patterns, filePaths) {
195
+ const normalizedPatterns = patterns.map(normalizeScopePattern);
196
+ const normalizedPaths = filePaths.map(normalizeScopePath);
197
+ const parsedPatterns = normalizedPatterns.map(parseScopePattern);
198
+ return normalizedPaths.map((normalizedPath) => {
199
+ const pathDecisions = parsedPatterns.flatMap((parsed, index) => {
200
+ if (!patternMatchesPath(parsed, normalizedPath))
201
+ return [];
202
+ const effect = parsed.negative ? "exclude" : "include";
203
+ return [{ index, pattern: normalizedPatterns[index], effect, matchedAfter: effect === "include" }];
121
204
  });
122
- child.stdin.end(`${paths.join("\0")}\0`);
205
+ const decisivePattern = pathDecisions.at(-1);
206
+ const hasPositiveMatch = pathDecisions.some((decision) => decision.effect === "include");
207
+ const matched = decisivePattern?.matchedAfter ?? false;
208
+ return {
209
+ path: normalizedPath,
210
+ patterns: pathDecisions,
211
+ matched,
212
+ outcome: matched ? "matched" : hasPositiveMatch ? "excluded" : "no-positive-match",
213
+ ...(decisivePattern ? { decisivePattern } : {}),
214
+ };
123
215
  });
124
216
  }
125
217
  export async function pathsMatchingScope(cwd, patterns, filePaths) {
126
- const normalizedPatterns = patterns.map(normalizeScopePattern);
127
- const normalizedPaths = filePaths.map(normalizeScopePath);
128
- if (normalizedPatterns.length === 0 || normalizedPaths.length === 0)
129
- return new Set();
130
- const temporaryRoot = await fs.mkdtemp(path.join(os.tmpdir(), "keiyaku-scope-"));
131
- const gitDir = path.join(temporaryRoot, "scope.git");
132
- const workTree = path.join(temporaryRoot, "worktree");
133
- try {
134
- await Promise.all([fs.mkdir(gitDir), fs.mkdir(workTree)]);
135
- await createGit(gitDir).init(true);
136
- await fs.writeFile(path.join(gitDir, "info", "exclude"), `${normalizedPatterns.join("\n")}\n`, "utf8");
137
- return await runCheckIgnore(gitDir, workTree, cwd, normalizedPaths);
138
- }
139
- finally {
140
- await fs.rm(temporaryRoot, { recursive: true, force: true });
141
- }
218
+ const evidence = await scopePathEvidence(cwd, patterns, filePaths);
219
+ return new Set(evidence.filter((item) => item.matched).map((item) => item.path));
142
220
  }
143
221
  export function isNegativeScopePattern(pattern) {
144
222
  return normalizeScopePattern(pattern).startsWith("!");
145
223
  }
146
224
  /**
147
- * Parse only the subset whose path language can be proved statically: literal
148
- * segments, `*` segments, and `**` segments. Anything using richer gitignore
149
- * syntax is left to Git and produces no advisory warning.
225
+ * Parse only the positive subset whose path language can be proved statically:
226
+ * literal segments, `*` segments, and `**` segments.
150
227
  */
151
228
  function staticScopeSegments(pattern) {
152
229
  const positive = normalizeScopePattern(pattern);
@@ -1,12 +1,12 @@
1
- import * as fs from "node:fs/promises";
2
- import * as path from "node:path";
3
1
  import { FlowError } from "../flow-error.js";
4
- import { createGit, wrapGitError } from "../git/core.js";
2
+ import { createGit, parseRevListOutput, wrapGitError } from "../git/core.js";
5
3
  import { KEIYAKU_FILE } from "../keiyaku.js";
4
+ import { formatLedgerEntryCommitMessage } from "./entry.js";
6
5
  import { activeLedgerRef, appendWithRetry, readLedger } from "./ledger.js";
7
- import { renderContractView } from "./contract-view.js";
8
- import { normalizeScopePath, pathsMatchingScope } from "./scope.js";
6
+ import { refreshContractView } from "./contract-view.js";
7
+ import { normalizeScopePath, scopePathEvidence, } from "./scope.js";
9
8
  import { deriveContractState, isTerminalState } from "./status/lifecycle.js";
9
+ import { casUpdateRefLog, createRefLogCommit } from "./ref-log.js";
10
10
  function findBind(entries) {
11
11
  const bind = entries.find((entry) => entry.kind === "bind");
12
12
  if (!bind)
@@ -64,10 +64,7 @@ async function resolveCommit(cwd, revision) {
64
64
  async function revList(cwd, from, to) {
65
65
  const git = createGit(cwd);
66
66
  try {
67
- return (await git.raw(["rev-list", "--reverse", `${from}..${to}`]))
68
- .split(/\r?\n/)
69
- .map((line) => line.trim())
70
- .filter(Boolean);
67
+ return parseRevListOutput(await git.raw(["rev-list", "--reverse", `${from}..${to}`]));
71
68
  }
72
69
  catch (error) {
73
70
  throw wrapGitError(`rev-list ${from}..${to}`, error, cwd);
@@ -104,8 +101,37 @@ async function diffstat(cwd, from, to) {
104
101
  }
105
102
  async function scopeViolations(cwd, paths, scope) {
106
103
  const auditedPaths = paths.filter((changedPath) => changedPath !== KEIYAKU_FILE);
107
- const matched = await pathsMatchingScope(cwd, scope, auditedPaths);
108
- return auditedPaths.filter((changedPath) => !matched.has(changedPath));
104
+ return (await scopePathEvidence(cwd, scope, auditedPaths)).filter((evidence) => !evidence.matched);
105
+ }
106
+ async function auditScope(cwd, entries, paths, waivePaths) {
107
+ const scope = effectiveScope(entries);
108
+ const scopeViolationEvidence = await scopeViolations(cwd, paths, scope);
109
+ const waivedPaths = new Set(waivePaths.map(normalizeScopePath));
110
+ const unwaived = scopeViolationEvidence.filter((violation) => !waivedPaths.has(violation.path));
111
+ if (unwaived.length > 0) {
112
+ throw new ScopeAuditError(findBind(entries).contract, scope, unwaived);
113
+ }
114
+ const violations = scopeViolationEvidence.map((violation) => violation.path);
115
+ return {
116
+ scopeAudit: { violations, waived: violations.length > 0 },
117
+ scopeViolationEvidence,
118
+ };
119
+ }
120
+ export class ScopeAuditError extends FlowError {
121
+ scopeViolationEvidence;
122
+ constructor(_contractId, scope, evidence) {
123
+ const count = evidence.length;
124
+ const fileLabel = count === 1 ? "file" : "files";
125
+ super("SCOPE_VIOLATION", `scope violation — ${count} ${fileLabel} outside declared scope`, {
126
+ facts: {
127
+ kind: "scope_violation",
128
+ scope: [...scope],
129
+ violations: evidence,
130
+ },
131
+ });
132
+ this.name = "ScopeAuditError";
133
+ this.scopeViolationEvidence = evidence;
134
+ }
109
135
  }
110
136
  function assertOperable(contractId, entries) {
111
137
  const state = deriveContractState(entries);
@@ -127,11 +153,10 @@ export function currentSealArc(entries) {
127
153
  }
128
154
  /** Stable read-only coordinate naming the receipt source a petition would seal. */
129
155
  export function petitionSealSource(entries) {
130
- const arc = currentSealArc(entries);
131
- const fence = getFence(entries);
132
- return arc === null
133
- ? `no open arc · fence ${fence}`
134
- : `a${arc} · fence ${fence}▸HEAD`;
156
+ return {
157
+ arc: currentSealArc(entries),
158
+ fence: getFence(entries),
159
+ };
135
160
  }
136
161
  /** Shared read-only owner for the seal capture and its compact changed-path evidence. */
137
162
  export async function previewSeal(cwd, entries, head = "HEAD", waivePaths = []) {
@@ -145,13 +170,7 @@ export async function previewSeal(cwd, entries, head = "HEAD", waivePaths = [])
145
170
  changedPaths(cwd, oldFence, newFence),
146
171
  diffstat(cwd, oldFence, newFence),
147
172
  ]);
148
- const scope = effectiveScope(entries);
149
- const violations = await scopeViolations(cwd, paths, scope);
150
- const waivedPaths = new Set(waivePaths.map(normalizeScopePath));
151
- const unwaived = violations.filter((violation) => !waivedPaths.has(violation));
152
- if (unwaived.length > 0) {
153
- throw new FlowError("INTERNAL_STATE", `scope audit failed for contract ${findBind(entries).contract}: paths ${unwaived.join(", ")} outside ${scope.join(", ")}`);
154
- }
173
+ const scopeAudit = await auditScope(cwd, entries, paths, waivePaths);
155
174
  return {
156
175
  capture: {
157
176
  arc,
@@ -159,7 +178,7 @@ export async function previewSeal(cwd, entries, head = "HEAD", waivePaths = [])
159
178
  newFence,
160
179
  receipts,
161
180
  diffstat: stat,
162
- scopeAudit: { violations, waived: violations.length > 0 },
181
+ ...scopeAudit,
163
182
  },
164
183
  paths,
165
184
  };
@@ -167,12 +186,30 @@ export async function previewSeal(cwd, entries, head = "HEAD", waivePaths = [])
167
186
  export async function captureSeal(cwd, entries, head = "HEAD", waivePaths = []) {
168
187
  return (await previewSeal(cwd, entries, head, waivePaths)).capture;
169
188
  }
170
- async function renderUpdatedContract(ledgerCwd, renderCwd, contractId) {
171
- const ledger = await readLedger(ledgerCwd, contractId);
172
- if (!ledger) {
173
- throw new FlowError("INTERNAL_STATE", `contract ${contractId} active ledger disappeared before render`);
174
- }
175
- await fs.writeFile(path.join(renderCwd, KEIYAKU_FILE), renderContractView({ id: contractId, entries: ledger.entries }), "utf8");
189
+ /** Preserve original receipt hashes while auditing the rebased delivery content. */
190
+ export async function captureRenewSeal(input) {
191
+ const arc = currentSealArc(input.entries);
192
+ if (arc === null)
193
+ return null;
194
+ const oldFence = getFence(input.entries);
195
+ const [receiptHead, contentBase, contentHead] = await Promise.all([
196
+ resolveCommit(input.cwd, input.receiptHead),
197
+ resolveCommit(input.cwd, input.contentBase),
198
+ resolveCommit(input.cwd, input.contentHead),
199
+ ]);
200
+ const [receipts, paths, stat] = await Promise.all([
201
+ revList(input.cwd, oldFence, receiptHead),
202
+ changedPaths(input.cwd, contentBase, contentHead),
203
+ diffstat(input.cwd, contentBase, contentHead),
204
+ ]);
205
+ return {
206
+ arc,
207
+ oldFence,
208
+ newFence: receiptHead,
209
+ receipts,
210
+ diffstat: stat,
211
+ ...await auditScope(input.cwd, input.entries, paths, input.waivePaths ?? []),
212
+ };
176
213
  }
177
214
  export async function sealCurrentArc(input) {
178
215
  const ledger = await readLedger(input.cwd, input.contractId);
@@ -196,25 +233,92 @@ export async function sealCurrentArc(input) {
196
233
  if (currentArc !== capture.arc || getFence(entries) !== capture.oldFence) {
197
234
  throw new FlowError("INTERNAL_STATE", `contract ${input.contractId} changed while sealing arc a${capture.arc}`);
198
235
  }
236
+ const { scopeViolationEvidence: _scopeViolationEvidence, ...durableCapture } = capture;
199
237
  return {
200
238
  v: 1,
201
239
  kind: "seal",
202
240
  contract: input.contractId,
203
241
  at,
204
242
  actor: "engine",
205
- data: { ...capture, sealedBy: input.sealedBy },
243
+ data: { ...durableCapture, sealedBy: input.sealedBy },
206
244
  };
207
245
  });
208
246
  if (!result.ok) {
209
247
  throw new FlowError("INTERNAL_STATE", `failed to seal arc for ${input.contractId}: ${result.reason}`);
210
248
  }
211
- await renderUpdatedContract(input.cwd, input.renderCwd, input.contractId);
249
+ await refreshContractView({
250
+ ledgerCwd: input.cwd,
251
+ renderCwd: input.renderCwd,
252
+ contractId: input.contractId,
253
+ });
212
254
  return {
213
255
  ...capture,
214
256
  sealedBy: input.sealedBy,
215
257
  ledgerHead: result.head,
216
- incrementalDiff: capture.receipts.length === 0
217
- ? "nothing sealed"
218
- : `${capture.receipts.length} receipt${capture.receipts.length === 1 ? "" : "s"} · +${capture.diffstat.insertions} −${capture.diffstat.deletions}`,
219
258
  };
220
259
  }
260
+ function splitLedgerMessage(entry) {
261
+ const message = formatLedgerEntryCommitMessage(entry);
262
+ const separator = message.indexOf("\n\n");
263
+ return { subject: message.slice(0, separator), body: message.slice(separator + 2).trimEnd() };
264
+ }
265
+ export async function appendRenewSettlement(input) {
266
+ const ledger = await readLedger(input.cwd, input.contractId);
267
+ if (!ledger || ledger.head !== input.expectedLedgerHead) {
268
+ throw new FlowError("CONCURRENT_MODIFICATION", `contract ${input.contractId} changed while renewing`);
269
+ }
270
+ assertOperable(input.contractId, ledger.entries);
271
+ if (input.capture && (currentSealArc(ledger.entries) !== input.capture.arc || getFence(ledger.entries) !== input.capture.oldFence)) {
272
+ throw new FlowError("CONCURRENT_MODIFICATION", `contract ${input.contractId} changed while renewing`);
273
+ }
274
+ const at = new Date(input.nowMs).toISOString();
275
+ let parentHead = ledger.head;
276
+ let sealHead;
277
+ if (input.capture) {
278
+ const { scopeViolationEvidence: _evidence, ...durableCapture } = input.capture;
279
+ const sealEntry = {
280
+ v: 1,
281
+ kind: "seal",
282
+ contract: input.contractId,
283
+ at,
284
+ actor: "engine",
285
+ data: { ...durableCapture, newFence: input.newHead, sealedBy: "renew" },
286
+ };
287
+ const message = splitLedgerMessage(sealEntry);
288
+ sealHead = await createRefLogCommit(input.cwd, {
289
+ ...message,
290
+ parentHead,
291
+ errorLabel: `ledger seal ${input.contractId}`,
292
+ });
293
+ parentHead = sealHead;
294
+ }
295
+ const renewEntry = {
296
+ v: 1,
297
+ kind: "renew",
298
+ contract: input.contractId,
299
+ at,
300
+ actor: input.actor,
301
+ data: {
302
+ oldBase: input.oldBase,
303
+ newBase: input.newBase,
304
+ oldHead: input.oldHead,
305
+ newHead: input.newHead,
306
+ },
307
+ };
308
+ const message = splitLedgerMessage(renewEntry);
309
+ const renewHead = await createRefLogCommit(input.cwd, {
310
+ ...message,
311
+ parentHead,
312
+ errorLabel: `ledger renew ${input.contractId}`,
313
+ });
314
+ if (!await casUpdateRefLog(input.cwd, activeLedgerRef(input.contractId), renewHead, ledger.head)) {
315
+ throw new FlowError("CONCURRENT_MODIFICATION", `contract ${input.contractId} changed while renewing`);
316
+ }
317
+ const sealed = input.capture ? {
318
+ ...input.capture,
319
+ newFence: input.newHead,
320
+ sealedBy: "renew",
321
+ ledgerHead: sealHead,
322
+ } : null;
323
+ return { ledgerHead: renewHead, sealed };
324
+ }
@@ -1,12 +1,13 @@
1
1
  import * as fs from "node:fs/promises";
2
2
  import * as path from "node:path";
3
- import { FlowError } from "../flow-error.js";
4
- import { createGit, errorContainsAnyPattern, MISSING_HEAD_PATTERNS, wrapGitError } from "../git/core.js";
5
- import { readLedger } from "./ledger.js";
6
- import { shortCommitHash } from "../git/commits.js";
7
- import { commissionTarget, latestBase } from "./status/drift.js";
8
- import { settleTaskBoardForLanding } from "./task-contract.js";
9
- import { stableRepoRoot } from "./worktree-path.js";
3
+ import { FlowError } from "../../flow-error.js";
4
+ import { syncDirectory } from "../../fs/durability.js";
5
+ import { createGit, errorContainsAnyPattern, MISSING_HEAD_PATTERNS, wrapGitError } from "../../git/core.js";
6
+ import { readLedger } from "../ledger.js";
7
+ import { shortCommitHash } from "../../git/commits.js";
8
+ import { commissionTarget, latestBase } from "../status/drift.js";
9
+ import { settleTaskBoardForLanding } from "../task/index.js";
10
+ import { stableRepoRoot } from "../worktree-path.js";
10
11
  const CLAIM_RECEIPTS_FILE = "claim-receipts.jsonl";
11
12
  async function appendClaimReceipt(cwd, receipt, restoreIfMissing = "") {
12
13
  const receiptPath = path.join(await stableRepoRoot(cwd), ".keiyaku", CLAIM_RECEIPTS_FILE);
@@ -28,13 +29,7 @@ async function appendClaimReceipt(cwd, receipt, restoreIfMissing = "") {
28
29
  finally {
29
30
  await handle.close();
30
31
  }
31
- const directory = await fs.open(path.dirname(receiptPath), "r");
32
- try {
33
- await directory.sync();
34
- }
35
- finally {
36
- await directory.close();
37
- }
32
+ await syncDirectory(path.dirname(receiptPath));
38
33
  }
39
34
  export async function resolveCommit(cwd, rev) {
40
35
  const git = createGit(cwd);
@@ -1,10 +1,11 @@
1
- import { FlowError } from "../flow-error.js";
2
- import { activeLedgerRef, appendWithRetry, readLedger } from "./ledger.js";
1
+ import { FlowError } from "../../flow-error.js";
2
+ import { assertCleanTrackedWorkingTree } from "../../git/worktree.js";
3
+ import { activeLedgerRef, appendWithRetry, readLedger } from "../ledger.js";
3
4
  import { assertClaimQueueHead, readQueueReadModel } from "./queue.js";
4
- import { releaseScope } from "./registry.js";
5
- import { deriveContractState } from "./status/lifecycle.js";
6
- import { commissionTarget, latestBase } from "./status/drift.js";
7
- import { cleanupContractWorkspace, findContractWorktreePath, stableRepoRoot } from "./worktree-path.js";
5
+ import { releaseScope } from "../registry.js";
6
+ import { deriveContractState } from "../status/lifecycle.js";
7
+ import { commissionTarget, latestBase } from "../status/drift.js";
8
+ import { cleanupContractWorkspace, findContractWorktreePath, stableRepoRoot } from "../worktree-path.js";
8
9
  import { defaultMergeDelivery, relativeDisplayPath, resolveCommit, resolveCommitOrNull, } from "./claim-delivery.js";
9
10
  async function cleanupClaim(cwd, contractId, actor, at) {
10
11
  const stableRoot = await stableRepoRoot(cwd);
@@ -14,16 +15,37 @@ async function cleanupClaim(cwd, contractId, actor, at) {
14
15
  function findClaim(entries) {
15
16
  return entries.find((entry) => entry.kind === "claim");
16
17
  }
18
+ /** Verdict belonging to the open petition window (after last petition, before claim). */
19
+ function latestCurrentPetitionVerdict(entries) {
20
+ for (let index = entries.length - 1; index >= 0; index -= 1) {
21
+ const entry = entries[index];
22
+ if (entry.kind === "verdict")
23
+ return entry;
24
+ if (entry.kind === "petition")
25
+ return undefined;
26
+ }
27
+ return undefined;
28
+ }
29
+ /**
30
+ * Delivery requires an approved verdict for the current petition.
31
+ * Already-claimed cleanup retry remains legal without re-proving judgment.
32
+ */
17
33
  function assertClaimableFromLedger(contractId, ledger) {
18
34
  if (!ledger) {
19
35
  throw new FlowError("INTERNAL_STATE", `contract ${contractId} not found`);
20
36
  }
21
37
  const state = deriveContractState(ledger.entries);
22
38
  const claim = findClaim(ledger.entries);
23
- if (state.state === "petitioned")
24
- return claim;
39
+ // Already-claimed cleanup retry: legal head even if a prior crash left debris.
25
40
  if (claim && state.state === "claimed")
26
41
  return claim;
42
+ if (state.state === "petitioned") {
43
+ const verdict = latestCurrentPetitionVerdict(ledger.entries);
44
+ if (verdict?.data.result !== "approved") {
45
+ throw new FlowError("INTERNAL_STATE", `contract ${contractId} cannot claim without approved verdict for current petition`);
46
+ }
47
+ return claim;
48
+ }
27
49
  if (state.state === "forfeited") {
28
50
  throw new FlowError("INTERNAL_STATE", `contract ${contractId} forfeited`);
29
51
  }
@@ -95,6 +117,8 @@ export async function previewClaimContract(input) {
95
117
  }
96
118
  export async function claimContract(input) {
97
119
  const at = new Date(input.nowMs).toISOString();
120
+ const deliveryWorktree = await findContractWorktreePath(input.cwd, input.contractId);
121
+ await assertCleanTrackedWorkingTree(deliveryWorktree ?? input.cwd);
98
122
  const existing = await readLedger(input.cwd, input.contractId);
99
123
  const existingClaim = assertClaimableFromLedger(input.contractId, existing);
100
124
  const existingState = existing ? deriveContractState(existing.entries) : undefined;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Public surface for the settlement package.
3
+ * Only symbols required by current external consumers are re-exported.
4
+ * Package-internal modules must import each other directly, not through this file.
5
+ *
6
+ * Orchestrator owns: gates → petition → pure verdict → claim delivery / reject publish.
7
+ * Foundations (queue, claim-delivery) may be depended on downward by stage modules.
8
+ * Stage modules (petition, verdict, claim) do not import one another.
9
+ */
10
+ export { claimKeiyaku, petitionKeiyaku, } from "./settlement.js";
11
+ export { buildPetitionClaimInput, petitionContract, loadPetitionPreflightContext, assertPetitionFresh, assertPetitionArcReady, assertPetitionable, } from "./petition.js";
12
+ export { previewPetition, PETITION_PREVIEW_GATE_NAMES, } from "./petition-preview.js";
13
+ export { verdictContract, } from "./verdict.js";
14
+ export { claimContract, previewClaimContract, } from "./claim.js";
15
+ export { defaultMergeDelivery, relativeDisplayPath, resolveCommit, resolveCommitOrNull, } from "./claim-delivery.js";
16
+ export { allocateSettlementSeat, assertClaimQueueHead, deriveQueueReadModel, queueMembership, readQueueReadModel, seatAllocatorRef, } from "./queue.js";
17
+ export { buildClaimGatePipeline, createAgentReviewGate, createOathGate, } from "./petition-claim-gates.js";
18
+ export { assertHeadUnchanged } from "./petition-head-guard.js";
19
+ export { forfeitKeiyaku } from "./petition-forfeit.js";
@@ -1,21 +1,20 @@
1
1
  import { spawn } from "node:child_process";
2
- import { getConfig } from "../config/env.js";
3
- import { assertSettingsKnobUsable } from "../config/settings/disease.js";
4
- import { resolveReviewerAgentName } from "../config/settings/knobs.js";
5
- import { loadKeiyakuSettings } from "../config/settings/loader.js";
6
- import { loadAkumaCatalog } from "../config/akuma-loader.js";
7
- import { formatAvailableAgentNames } from "../agents/selector.js";
8
- import { getDiffStats } from "../git/diff/preview.js";
9
- import { readDiff } from "../git/diff/read.js";
10
- import { getCurrentBranch } from "../git/branches.js";
11
- import { getLatestCommitHash } from "../git/staging.js";
12
- import { DIFF_PREVIEW_TEXT_MAX_CHARS } from "../keiyaku.js";
13
- import { FlowError } from "../flow-error.js";
14
- import { persistResponseHistory } from "./transcripts.js";
15
- import { logInfo } from "../telemetry/logger.js";
16
- import { execSubagent } from "./call/call.js";
17
- import { readContractLog } from "./log.js";
18
- import { buildVerificationFailureHints } from "./hints.js";
2
+ import { getConfig } from "../../config/env.js";
3
+ import { assertSettingsKnobUsable } from "../../config/settings/disease.js";
4
+ import { resolveReviewerAgentName } from "../../config/settings/knobs.js";
5
+ import { loadKeiyakuSettings } from "../../config/settings/loader.js";
6
+ import { loadAkumaCatalog } from "../../config/akuma-loader.js";
7
+ import { formatAvailableAgentNames } from "../../agents/selector.js";
8
+ import { getDiffStats } from "../../git/diff/preview.js";
9
+ import { readDiff } from "../../git/diff/read.js";
10
+ import { getCurrentBranch } from "../../git/branches.js";
11
+ import { getLatestCommitHash } from "../../git/staging.js";
12
+ import { DIFF_PREVIEW_TEXT_MAX_CHARS } from "../../keiyaku.js";
13
+ import { FlowError } from "../../flow-error.js";
14
+ import { persistResponseHistory } from "../transcripts.js";
15
+ import { logInfo } from "../../telemetry/logger.js";
16
+ import { execSubagent } from "../call/call.js";
17
+ import { readContractLog } from "../log.js";
19
18
  const CLAIM_VERIFICATION_OUTPUT_MAX_CHARS = 1200;
20
19
  const REVIEW_PASS_TOKEN = "PASS";
21
20
  const REVIEW_REJECTION_MESSAGE = "CLAIM rejected by independent reviewer.";
@@ -139,7 +138,12 @@ export function createCommandVerificationGate() {
139
138
  pass: false,
140
139
  code: "CLOSE_QUALITY_GATE_FAILED",
141
140
  message: `CLAIM blocked: verification failed at '${verification.label}' (exit ${result.code}).`,
142
- hints: buildVerificationFailureHints(verification.label, clipCommandOutput(result.stdout), clipCommandOutput(result.stderr)),
141
+ facts: {
142
+ kind: "verification_failure",
143
+ label: verification.label,
144
+ stdout: clipCommandOutput(result.stdout),
145
+ stderr: clipCommandOutput(result.stderr),
146
+ },
143
147
  };
144
148
  }
145
149
  }