@astrosheep/keiyaku 2.9.9 → 2.9.10

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 (230) hide show
  1. package/README.md +50 -182
  2. package/build/.tsbuildinfo +1 -1
  3. package/build/agents/harness/control-types.js +18 -1
  4. package/build/agents/harness/index.js +2 -0
  5. package/build/agents/harness/outcome-error.js +81 -0
  6. package/build/agents/harness/projection.js +2 -1
  7. package/build/agents/harness/router.js +5 -1
  8. package/build/agents/providers/claude-agent-sdk/adapter.js +18 -4
  9. package/build/agents/providers/claude-agent-sdk/registration.js +11 -1
  10. package/build/agents/providers/claude-agent-sdk/session.js +19 -5
  11. package/build/agents/providers/codex-app-server/adapter.js +11 -6
  12. package/build/agents/providers/codex-app-server/events.js +67 -6
  13. package/build/agents/providers/codex-app-server/registration.js +11 -1
  14. package/build/agents/providers/opencode-sdk/adapter.js +8 -1
  15. package/build/agents/providers/opencode-sdk/events.js +24 -2
  16. package/build/agents/providers/opencode-sdk/registration.js +11 -1
  17. package/build/agents/providers/pi/checkpoint.js +9 -1
  18. package/build/agents/providers/pi/events.js +2 -0
  19. package/build/agents/providers/pi/registration.js +14 -1
  20. package/build/cli/commands/akuma/akuma/handler.js +1 -1
  21. package/build/cli/commands/akuma/akuma/meta.js +3 -3
  22. package/build/cli/commands/akuma/catalog.js +2 -2
  23. package/build/cli/commands/akuma/{view → show}/handler.js +5 -5
  24. package/build/cli/commands/akuma/{view → show}/meta.js +3 -3
  25. package/build/cli/commands/akuma.js +1 -1
  26. package/build/cli/commands/contract/amend/handler.js +5 -4
  27. package/build/cli/commands/contract/arc/handler.js +5 -4
  28. package/build/cli/commands/contract/audit/handler.js +2 -2
  29. package/build/cli/commands/contract/bind/handler.js +12 -3
  30. package/build/cli/commands/contract/bind/meta.js +45 -15
  31. package/build/cli/commands/contract/forfeit/handler.js +9 -5
  32. package/build/cli/commands/contract/log/handler.js +2 -2
  33. package/build/cli/commands/contract/petition/handler.js +34 -7
  34. package/build/cli/commands/contract/renew/handler.js +7 -7
  35. package/build/cli/commands/contract/renew/meta.js +3 -3
  36. package/build/cli/commands/metadata.js +10 -6
  37. package/build/cli/commands/projection/call/meta.js +1 -1
  38. package/build/cli/commands/projection/catalog.js +2 -0
  39. package/build/cli/commands/projection/history/handler.js +21 -0
  40. package/build/cli/commands/projection/history/meta.js +9 -0
  41. package/build/cli/commands/projection/shared.js +3 -4
  42. package/build/cli/commands/projection/status/handler.js +3 -3
  43. package/build/cli/commands/projection/wait/handler.js +20 -11
  44. package/build/cli/commands/task/add/handler.js +14 -5
  45. package/build/cli/commands/task/add/meta.js +2 -2
  46. package/build/cli/commands/task/catalog.js +4 -0
  47. package/build/cli/commands/task/done/handler.js +1 -4
  48. package/build/cli/commands/task/done/meta.js +1 -1
  49. package/build/cli/commands/task/drop/meta.js +1 -1
  50. package/build/cli/commands/task/hold/handler.js +3 -0
  51. package/build/cli/commands/task/hold/meta.js +8 -0
  52. package/build/cli/commands/task/log/handler.js +1 -1
  53. package/build/cli/commands/task/log/meta.js +2 -2
  54. package/build/cli/commands/task/resume/handler.js +3 -0
  55. package/build/cli/commands/task/resume/meta.js +8 -0
  56. package/build/cli/commands/task/shared.js +39 -14
  57. package/build/cli/commands/task/show/handler.js +1 -1
  58. package/build/cli/commands/task/show/meta.js +1 -1
  59. package/build/cli/commands/task/start/meta.js +1 -1
  60. package/build/cli/commands/task/stop/handler.js +1 -4
  61. package/build/cli/commands/task/stop/meta.js +1 -1
  62. package/build/cli/commands/task/update/handler.js +11 -3
  63. package/build/cli/commands/task/update/meta.js +3 -3
  64. package/build/cli/completion.js +3 -2
  65. package/build/cli/flags.js +51 -14
  66. package/build/cli/help.js +4 -1
  67. package/build/cli/index.js +40 -25
  68. package/build/cli/parse-flags.js +40 -7
  69. package/build/cli/parse-metadata.js +5 -2
  70. package/build/cli/parse-selectors.js +2 -3
  71. package/build/cli/parse.js +40 -6
  72. package/build/cli/projection-address.js +12 -10
  73. package/build/cli/render/amendment-warning.js +8 -0
  74. package/build/cli/render/arc.js +6 -2
  75. package/build/cli/render/audit.js +400 -81
  76. package/build/cli/render/kanshi.js +6 -1
  77. package/build/cli/render/lifecycle.js +56 -0
  78. package/build/cli/render/misc.js +24 -28
  79. package/build/cli/render/petition.js +5 -0
  80. package/build/cli/render/projection-activity.js +3 -2
  81. package/build/cli/render/projection-history.js +64 -0
  82. package/build/cli/render/shared.js +23 -9
  83. package/build/cli/render/status.js +9 -16
  84. package/build/cli/render/task.js +51 -26
  85. package/build/cli/render/terminal-failure.js +3 -1
  86. package/build/cli/render/wait.js +3 -5
  87. package/build/cli/subagent-guard.js +1 -1
  88. package/build/cli/types.js +1 -1
  89. package/build/config/env-keys.js +2 -0
  90. package/build/config/env.js +23 -0
  91. package/build/config/settings/schema.js +3 -1
  92. package/build/core/address-carrier.js +89 -0
  93. package/build/core/addressing.js +255 -196
  94. package/build/core/amend.js +115 -38
  95. package/build/core/amendment-criteria.js +17 -0
  96. package/build/core/amendment-warning.js +10 -0
  97. package/build/core/arc.js +184 -83
  98. package/build/core/audit/candidate.js +110 -9
  99. package/build/core/audit/coordinates.js +120 -65
  100. package/build/core/audit/evidence.js +19 -23
  101. package/build/core/audit/facade.js +34 -11
  102. package/build/core/audit/report.js +5 -5
  103. package/build/core/bind-reconciliation.js +390 -0
  104. package/build/core/bind-workspace.js +12 -20
  105. package/build/core/bind.js +242 -273
  106. package/build/core/call/call.js +1 -0
  107. package/build/core/call/context.js +46 -71
  108. package/build/core/call/prompt.js +6 -3
  109. package/build/core/context.js +49 -35
  110. package/build/core/contract-carrier-runtime.js +408 -0
  111. package/build/core/contract-carrier.js +270 -0
  112. package/build/core/contract-view.js +33 -27
  113. package/build/core/contract.js +5 -1
  114. package/build/core/derived-replay.js +207 -0
  115. package/build/core/draft.js +125 -57
  116. package/build/core/forfeit.js +113 -56
  117. package/build/core/identifier-slug.js +63 -13
  118. package/build/core/ids.js +4 -4
  119. package/build/core/lifecycle-history.js +35 -0
  120. package/build/core/lifecycle-recovery.js +117 -0
  121. package/build/core/lifecycle-runner.js +377 -0
  122. package/build/core/log.js +37 -11
  123. package/build/core/outcome-base.js +181 -0
  124. package/build/core/process-group.js +139 -0
  125. package/build/core/projection/generation/database.js +82 -7
  126. package/build/core/projection/generation/model.js +9 -36
  127. package/build/core/projection/generation/projection-generation-continuation.js +10 -22
  128. package/build/core/projection/generation/projection-generation-execution.js +6 -5
  129. package/build/core/projection/index.js +5 -4
  130. package/build/core/projection/projection-activity.js +27 -8
  131. package/build/core/projection/projection-alias.js +23 -10
  132. package/build/core/projection/projection-core.js +21 -7
  133. package/build/core/projection/projection-execution-observer.js +91 -39
  134. package/build/core/projection/projection-history.js +474 -0
  135. package/build/core/projection/projection-runner-lock.js +12 -0
  136. package/build/core/projection/projection-status-observation.js +365 -0
  137. package/build/core/projection/projection-status.js +21 -358
  138. package/build/core/projection/projection-terminal-failure.js +13 -8
  139. package/build/core/projection/projection-wait.js +7 -2
  140. package/build/core/projection/tell/launch-store.js +244 -0
  141. package/build/core/projection/tell/model.js +3 -3
  142. package/build/core/projection/tell/store.js +70 -118
  143. package/build/core/registry.js +22 -87
  144. package/build/core/renew-build.js +424 -0
  145. package/build/core/renew-plan.js +239 -0
  146. package/build/core/renew-rewrite.js +77 -0
  147. package/build/core/renew.js +295 -520
  148. package/build/core/repository-ledger/accepted-fold-read.js +19 -0
  149. package/build/core/repository-ledger/accepted-tail.js +116 -0
  150. package/build/core/repository-ledger/admission.js +38 -0
  151. package/build/core/repository-ledger/authoritative-transaction.js +1 -0
  152. package/build/core/repository-ledger/claim-evidence.js +147 -0
  153. package/build/core/repository-ledger/codec.js +297 -0
  154. package/build/core/repository-ledger/current-state-store.js +853 -0
  155. package/build/core/repository-ledger/fold-repository.js +375 -0
  156. package/build/core/repository-ledger/fold.js +248 -0
  157. package/build/core/repository-ledger/git-object-store.js +243 -0
  158. package/build/core/repository-ledger/history-read.js +15 -0
  159. package/build/core/repository-ledger/incremental-read.js +153 -0
  160. package/build/core/repository-ledger/index.js +15 -0
  161. package/build/core/repository-ledger/inventory-plan.js +75 -0
  162. package/build/core/repository-ledger/inventory.js +310 -0
  163. package/build/core/repository-ledger/publication-admission.js +125 -0
  164. package/build/core/repository-ledger/publication.js +353 -0
  165. package/build/core/repository-ledger/read-model.js +469 -0
  166. package/build/core/repository-ledger/transaction-membership.js +78 -0
  167. package/build/core/repository-ledger/traversal.js +404 -0
  168. package/build/core/repository-ledger/write-transaction.js +339 -0
  169. package/build/core/seal.js +168 -161
  170. package/build/core/settlement/claim-delivery.js +42 -215
  171. package/build/core/settlement/claim.js +235 -153
  172. package/build/core/settlement/index.js +4 -4
  173. package/build/core/settlement/petition-claim-gates.js +52 -33
  174. package/build/core/settlement/petition-forfeit.js +12 -5
  175. package/build/core/settlement/petition-head-guard.js +8 -5
  176. package/build/core/settlement/petition-preview.js +68 -56
  177. package/build/core/settlement/petition-window.js +50 -0
  178. package/build/core/settlement/petition.js +252 -131
  179. package/build/core/settlement/queue-read-model.js +72 -95
  180. package/build/core/settlement/queue-seat-allocation.js +35 -126
  181. package/build/core/settlement/queue.js +1 -1
  182. package/build/core/settlement/settlement.js +172 -80
  183. package/build/core/settlement/verdict.js +112 -66
  184. package/build/core/settlement/verification-coordination.js +305 -0
  185. package/build/core/settlement/verification-supervisor.js +275 -0
  186. package/build/core/settlement/verification.js +398 -31
  187. package/build/core/status/board.js +114 -119
  188. package/build/core/status/lifecycle.js +7 -80
  189. package/build/core/structured-query.js +23 -0
  190. package/build/core/target-ref.js +1 -1
  191. package/build/core/task/board.js +59 -24
  192. package/build/core/task/commands.js +181 -33
  193. package/build/core/task/coordinate.js +56 -0
  194. package/build/core/task/document.js +25 -9
  195. package/build/core/task/index.js +7 -4
  196. package/build/core/task/query.js +17 -27
  197. package/build/core/task/settlement-git.js +164 -179
  198. package/build/core/task/settlement-policy.js +15 -11
  199. package/build/core/task/source-board.js +31 -13
  200. package/build/core/task/task-bind-preparation.js +193 -0
  201. package/build/core/task/task-contract.js +102 -193
  202. package/build/core/task/task-file-transaction.js +171 -0
  203. package/build/core/task/task-git-runtime.js +76 -14
  204. package/build/core/task/task-git-store.js +68 -169
  205. package/build/core/task/task-store-repository.js +13 -6
  206. package/build/core/task/task-worktree.js +6 -33
  207. package/build/core/task/task.js +1 -1
  208. package/build/core/task/tree.js +3 -3
  209. package/build/core/task/validation.js +4 -3
  210. package/build/core/transcripts.js +93 -1
  211. package/build/core/worktree-bootstrap.js +44 -36
  212. package/build/core/worktree-path.js +210 -77
  213. package/build/flow-error.js +7 -7
  214. package/build/generated/version.js +2 -2
  215. package/build/git/core.js +76 -13
  216. package/build/git/refs.js +168 -46
  217. package/build/git/streaming-batch.js +73 -13
  218. package/package.json +1 -1
  219. package/skills/keiyaku/SKILL.md +1 -1
  220. package/skills/keiyaku-akuma/SKILL.md +3 -3
  221. package/skills/keiyaku-task/SKILL.md +4 -2
  222. package/skills/keiyaku-workflow/SKILL.md +88 -203
  223. package/build/core/entry.js +0 -305
  224. package/build/core/ledger-batch.js +0 -194
  225. package/build/core/ledger.js +0 -84
  226. package/build/core/projection/leash.js +0 -87
  227. package/build/core/ref-log.js +0 -119
  228. package/build/core/renew-session.js +0 -128
  229. package/build/core/status/ledger-batch.js +0 -1
  230. package/build/core/status/reconciliation.js +0 -137
@@ -1,114 +1,433 @@
1
1
  import { asMessage, FlowError } from "../../flow-error.js";
2
- function streamLines(name, result) {
2
+ import { shortCommitHash } from "../../git/commits.js";
3
+ function shortRef(ref) {
4
+ return ref.replace(/^refs\/heads\//, "");
5
+ }
6
+ function formatDuration(ms) {
7
+ if (ms < 1000)
8
+ return `${ms}ms`;
9
+ const seconds = ms / 1000;
10
+ if (seconds < 60)
11
+ return `${seconds.toFixed(seconds < 10 ? 1 : 0)}s`;
12
+ const minutes = Math.floor(seconds / 60);
13
+ const rem = Math.round(seconds % 60);
14
+ return `${minutes}m${rem.toString().padStart(2, "0")}s`;
15
+ }
16
+ function formatBytes(bytes) {
17
+ if (bytes < 1024)
18
+ return `${bytes} bytes`;
19
+ const kib = bytes / 1024;
20
+ if (kib < 1024)
21
+ return `${kib < 10 ? kib.toFixed(1) : Math.round(kib)} KiB`;
22
+ const mib = kib / 1024;
23
+ return `${mib < 10 ? mib.toFixed(1) : Math.round(mib)} MiB`;
24
+ }
25
+ function padLabel(label, width) {
26
+ return label.length >= width ? label : `${label}${" ".repeat(width - label.length)}`;
27
+ }
28
+ function scopeMatchLabel(file) {
29
+ if (file.matcher.decisivePattern) {
30
+ const effect = file.matcher.decisivePattern.effect === "exclude" ? "excluded by" : "matched";
31
+ return `${effect}: ${file.matcher.decisivePattern.pattern}`;
32
+ }
33
+ if (file.class === "downgraded")
34
+ return "no declared term · explained companions only";
35
+ return "no declared term";
36
+ }
37
+ function explanationLine(item) {
38
+ if (item.kind === "file-rename")
39
+ return `rename ${item.from} -> ${item.to} (${item.similarity}%)`;
40
+ if (item.kind === "symbol-rename") {
41
+ return `symbol rename ${item.from} -> ${item.to} · h${item.hunk} old:${item.oldLine ?? "-"} new:${item.newLine ?? "-"}`;
42
+ }
43
+ return `import · h${item.hunk} old:${item.oldLine ?? "-"} new:${item.newLine ?? "-"}`;
44
+ }
45
+ function residueSummary(file) {
46
+ if (file.class !== "violated" || file.residue.length === 0)
47
+ return null;
48
+ const hunks = [...new Set(file.residue.map((item) => item.hunk).filter((hunk) => hunk >= 0))].sort((a, b) => a - b);
49
+ if (file.residue.some((item) => item.kind === "binary")) {
50
+ return `unexplained: binary content changed`;
51
+ }
52
+ if (hunks.length === 0)
53
+ return `unexplained: ${file.residue.length} changes`;
54
+ return `unexplained: ${file.residue.length} lines across hunk ${hunks.join(", ")}`;
55
+ }
56
+ function indentBlock(text, prefix = " ") {
57
+ return text.split("\n").map((line) => `${prefix}${line}`);
58
+ }
59
+ function selectedFor(file, report) {
60
+ return report.presentation?.selected.filter((item) => item.path === file.path) ?? [];
61
+ }
62
+ function omissionsFor(file, report) {
63
+ return report.presentation?.omissions.filter((item) => item.path === file.path) ?? [];
64
+ }
65
+ function renderFileBlock(file, report) {
66
+ const rename = file.oldPath ? `${file.oldPath} -> ` : "";
67
+ const header = ` ${file.status} ${rename}${file.path}`.padEnd(62) + `+${file.stats.insertions} -${file.stats.deletions}`;
68
+ const lines = [header, ` ${scopeMatchLabel(file)}`];
69
+ for (const explanation of file.explanations)
70
+ lines.push(` ${explanationLine(explanation)}`);
71
+ const residue = residueSummary(file);
72
+ if (residue)
73
+ lines.push(` ${residue}`);
74
+ const selected = selectedFor(file, report);
75
+ const omissions = omissionsFor(file, report);
76
+ for (const item of selected) {
77
+ if (item.hunk === null)
78
+ continue;
79
+ lines.push("");
80
+ lines.push(...indentBlock(item.content));
81
+ }
82
+ if (omissions.length > 0) {
83
+ const omittedHunks = omissions.filter((item) => item.hunk !== null).length;
84
+ const omittedBytes = omissions.reduce((sum, item) => sum + item.bytes, 0);
85
+ const subject = omissions.every((item) => item.hunk === null)
86
+ ? "file content omitted"
87
+ : `${omittedHunks} hunk${omittedHunks === 1 ? "" : "s"} omitted`;
88
+ lines.push("");
89
+ lines.push(` [${subject} · ${formatBytes(omittedBytes)}]`);
90
+ }
91
+ return lines;
92
+ }
93
+ function classRank(value) {
94
+ if (value === "violated")
95
+ return 0;
96
+ if (value === "downgraded")
97
+ return 1;
98
+ return 2;
99
+ }
100
+ function orderedFiles(report) {
101
+ return [...(report.files ?? [])].sort((left, right) => {
102
+ const rank = classRank(left.class) - classRank(right.class);
103
+ return rank || left.path.localeCompare(right.path);
104
+ });
105
+ }
106
+ function hasCoordinateRace(report) {
107
+ return report.findings.some((finding) => finding.code === "COORDINATE_RACE");
108
+ }
109
+ function isMatchedScope(file) {
110
+ return file.class === "in-scope" || file.matcher.matched;
111
+ }
112
+ function renderChanges(report) {
113
+ if (!report.diff || !report.presentation || !report.files) {
114
+ return [hasCoordinateRace(report) || report.rehearsal.status === "conflict" || report.rehearsal.status === "unknown" ? "—" : "not evaluated"];
115
+ }
116
+ const files = orderedFiles(report);
117
+ const outside = files.filter((file) => !isMatchedScope(file));
118
+ const declared = files.filter((file) => isMatchedScope(file));
119
+ const lines = [
120
+ `${report.diff.stat.filesChanged} files · +${report.diff.stat.insertions} -${report.diff.stat.deletions}`,
121
+ ];
122
+ if (outside.length > 0) {
123
+ lines.push("", "Outside declared write set", "");
124
+ for (const file of outside) {
125
+ lines.push(...renderFileBlock(file, report), "");
126
+ }
127
+ }
128
+ if (declared.length > 0) {
129
+ lines.push("", "Declared changes", "");
130
+ for (const file of declared) {
131
+ lines.push(...renderFileBlock(file, report), "");
132
+ }
133
+ }
134
+ const selectedCount = report.presentation.selected.filter((item) => item.hunk !== null).length;
135
+ const omittedCount = report.presentation.omissions.filter((item) => item.hunk !== null).length;
136
+ const totalHunks = selectedCount + omittedCount;
137
+ if (totalHunks > 0 || report.presentation.budget.limit === 0) {
138
+ lines.push(`Shown ${selectedCount} of ${totalHunks || selectedCount} hunks · ${formatBytes(report.presentation.budget.limit)} presentation limit`);
139
+ }
140
+ while (lines.at(-1) === "")
141
+ lines.pop();
142
+ return lines;
143
+ }
144
+ function shouldExpandResult(result) {
145
+ if (result.status === "runner_failed")
146
+ return true;
147
+ if (result.status === "executed" && result.exitCode !== 0)
148
+ return true;
149
+ return [result.stdout, result.stderr].some((stream) => stream.truncated || stream.tail.length > 0);
150
+ }
151
+ function streamDetail(name, result, forceEmpty) {
3
152
  const stream = result[name];
4
- return [
5
- `${name} tail (${stream.truncated ? `truncated, ${stream.omittedBytes} bytes omitted` : "complete, 0 bytes omitted"}):`,
6
- stream.tail || "(empty)",
153
+ if (!forceEmpty && !stream.truncated && stream.tail.length === 0)
154
+ return [];
155
+ const mark = stream.truncated ? `truncated · ${formatBytes(stream.omittedBytes)} omitted` : "complete";
156
+ return [` ${name} (${mark})`, ...indentBlock(stream.tail || "(empty)", " ")];
157
+ }
158
+ function verificationStatus(result) {
159
+ if (result.status === "runner_failed")
160
+ return "unknown";
161
+ return result.exitCode === 0 ? "passed" : "failed";
162
+ }
163
+ function sourceHeading(source) {
164
+ return source === "contract" ? "contract" : "repository gate";
165
+ }
166
+ function preparationReason(reason) {
167
+ if (reason === "verification_not_configured")
168
+ return "no verification plan";
169
+ if (reason === "verification_plan_unavailable")
170
+ return "verification plan unavailable";
171
+ if (reason === "coordinate_race")
172
+ return "coordinate change";
173
+ return "candidate unavailable";
174
+ }
175
+ function preparationResult(result) {
176
+ if (result.kind === "exit")
177
+ return `exit ${result.code}`;
178
+ if (result.kind === "signal")
179
+ return `signal ${result.signal}`;
180
+ if (result.kind === "timeout")
181
+ return "timeout";
182
+ return `spawn · ${result.message}`;
183
+ }
184
+ function preparationStreams(preparation) {
185
+ const lines = [];
186
+ for (const stream of ["stdout", "stderr"]) {
187
+ const tail = preparation[`${stream}Tail`];
188
+ const truncated = preparation[`${stream}Truncated`];
189
+ if (tail === undefined && !truncated)
190
+ continue;
191
+ lines.push(` ${stream} (${truncated ? "truncated" : "complete"})`);
192
+ lines.push(...indentBlock(tail || "(empty)", " "));
193
+ }
194
+ return lines;
195
+ }
196
+ function renderCandidatePreparation(report) {
197
+ const preparation = report.candidatePreparation;
198
+ if (preparation.status === "not-required")
199
+ return [` preparation not required · ${preparationReason(preparation.reason)}`];
200
+ if (preparation.status === "not-configured")
201
+ return [" preparation not configured · no bind bootstrap plan"];
202
+ const command = JSON.stringify(preparation.plan.command);
203
+ if (preparation.status === "succeeded") {
204
+ return [
205
+ ` preparation passed ${padLabel(formatDuration(preparation.durationMs), 6)} ${command}`,
206
+ ...preparationStreams(preparation),
207
+ ];
208
+ }
209
+ const lines = [
210
+ ` preparation failed ${padLabel(formatDuration(preparation.durationMs), 6)} ${preparationResult(preparation.result)}`,
211
+ ` ${preparation.cause.code}: ${preparation.cause.message}`,
212
+ ` argv ${command} · timeout ${preparation.plan.timeoutMs}ms`,
7
213
  ];
214
+ if (preparation.integrity) {
215
+ lines.push(` integrity head=${preparation.integrity.head ?? "unknown"} · tree=${preparation.integrity.tree ?? "unknown"} · tracked=${preparation.integrity.trackedChanges.length}`);
216
+ }
217
+ lines.push(...preparationStreams(preparation));
218
+ return lines;
8
219
  }
9
- function renderVerification(report) {
220
+ function renderVerificationResults(report) {
10
221
  if (report.verification.status === "not_configured")
11
222
  return ["not configured"];
12
- if (report.verification.status === "not_evaluated")
13
- return [`not evaluated: ${report.verification.reason}`];
14
- if (report.verification.status === "unknown")
15
- return [`unknown: ${report.verification.cause.code}: ${report.verification.cause.message}`];
223
+ if (report.verification.status === "not_evaluated") {
224
+ if (hasCoordinateRace(report) || report.verification.reason === "renew_conflict" || report.verification.reason === "renew_unknown") {
225
+ return ["—"];
226
+ }
227
+ return [`not evaluated · ${report.verification.reason.replaceAll("_", " ")}`];
228
+ }
229
+ if (report.verification.status === "unknown") {
230
+ return [`unknown · ${report.verification.cause.code}: ${report.verification.cause.message}`];
231
+ }
232
+ const labels = report.verification.results.map((result) => result.label);
233
+ const width = Math.max(8, ...labels.map((label) => label.length));
16
234
  const lines = [];
17
- for (const result of report.verification.results) {
18
- lines.push(`command ${result.label}`, `exec ${result.command}`, `argv ${JSON.stringify(result.argv)}`, `head ${result.head}`);
19
- if (result.status === "unverifiable") {
20
- lines.push(`not executable: ${result.cause}`, "");
235
+ const groups = ["contract", "repository-gate"];
236
+ for (const source of groups) {
237
+ const results = report.verification.results.filter((result) => result.source === source);
238
+ if (results.length === 0)
21
239
  continue;
240
+ lines.push(` ${sourceHeading(source)}`);
241
+ for (const result of results) {
242
+ const status = padLabel(verificationStatus(result), 7);
243
+ const duration = formatDuration(result.durationMs);
244
+ lines.push(` ${padLabel(result.label, width)} ${status} ${padLabel(duration, 6)} ${result.declaration}`);
245
+ if (result.status === "runner_failed") {
246
+ lines.push(` ${result.cause.code}: ${result.cause.message}`);
247
+ }
248
+ if (shouldExpandResult(result)) {
249
+ const forceEmpty = result.status === "runner_failed" || (result.status === "executed" && result.exitCode !== 0);
250
+ lines.push(...streamDetail("stdout", result, forceEmpty), ...streamDetail("stderr", result, forceEmpty));
251
+ }
22
252
  }
23
- lines.push(`\`\`\`${result.executor}`, result.body, "\`\`\`", `exit ${result.exitCode} · duration ${result.durationMs}ms`, ...streamLines("stdout", result), ...streamLines("stderr", result), "");
24
253
  }
25
254
  return lines;
26
255
  }
27
- function renderScope(report) {
28
- if (!report.scope)
29
- return ["not evaluated"];
30
- return report.scope.flatMap((row) => {
31
- const decisive = row.matcher.decisivePattern
32
- ? `rule ${row.matcher.decisivePattern.index}:${row.matcher.decisivePattern.pattern} (${row.matcher.decisivePattern.effect})`
33
- : `rule none (${row.matcher.outcome})`;
34
- const explanations = row.explanations.map((item) => {
35
- if (item.kind === "file-rename")
36
- return ` explain file rename ${item.from} -> ${item.to} (${item.similarity}%)`;
37
- if (item.kind === "symbol-rename")
38
- return ` explain symbol rename ${item.from} -> ${item.to} at h${item.hunk} old:${item.oldLine ?? "-"} new:${item.newLine ?? "-"}`;
39
- return ` explain import at h${item.hunk} old:${item.oldLine ?? "-"} new:${item.newLine ?? "-"}`;
40
- });
41
- const residue = row.residue.map((item) => ` residue h${item.hunk} old:${item.oldLine ?? "-"} new:${item.newLine ?? "-"} ${item.kind} ${item.content}`);
42
- return [`${row.class} ${row.path} · ${decisive}`, ...explanations, ...residue];
43
- });
256
+ function renderVerification(report) {
257
+ const preparation = renderCandidatePreparation(report);
258
+ const results = renderVerificationResults(report);
259
+ return [...preparation, "", ...results];
260
+ }
261
+ function residueParts(readiness) {
262
+ const residue = readiness.deliveryResidue;
263
+ const parts = [];
264
+ if (residue.staged.length > 0)
265
+ parts.push(`${residue.staged.length} staged`);
266
+ if (residue.unstaged.length > 0)
267
+ parts.push(`${residue.unstaged.length} unstaged`);
268
+ if (residue.untracked.length > 0)
269
+ parts.push(`${residue.untracked.length} untracked`);
270
+ if (parts.length === 0 && residue.tracked.length > 0)
271
+ parts.push(`${residue.tracked.length} tracked dirty`);
272
+ return parts.join(" · ");
44
273
  }
45
- function renderReadiness(report) {
274
+ function renderDeliveryContext(report) {
275
+ const race = report.findings.find((finding) => finding.code === "COORDINATE_RACE");
276
+ if (race)
277
+ return renderCoordinateRace(race);
46
278
  const readiness = report.readiness;
47
279
  if (!readiness)
48
- return ["not evaluated: coordinate race"];
49
- return [
50
- `delivery residue tracked=${readiness.deliveryResidue.tracked.length} staged=${readiness.deliveryResidue.staged.length} unstaged=${readiness.deliveryResidue.unstaged.length} untracked=${readiness.deliveryResidue.untracked.length}`,
51
- `arc open=${readiness.arc.open ?? "none"} max=${readiness.arc.max} fence=${readiness.arc.fence} loose=${readiness.arc.looseCommits.length}`,
52
- `renew session=${readiness.renewSession.open ? "open" : "none"}`,
53
- `rewrite guard=${readiness.rewriteGuard.liveProjection ? "live projection" : "clear"}`,
54
- ...readiness.dependencies.map((item) => `dependency ${item.id} ${item.satisfied ? "satisfied" : `open (${item.state})`}`),
55
- `petition state=${readiness.petition.state}${readiness.petition.queue ? ` queue=${readiness.petition.queue.position ?? "?"}/${readiness.petition.queue.length}` : " queue=none"}`,
56
- ];
280
+ return ["not evaluated"];
281
+ const lines = [];
282
+ const residue = residueParts(readiness);
283
+ lines.push(` worktree ${residue || "clean"}`);
284
+ const arcBits = [`max ${readiness.arc.max}`];
285
+ if (readiness.arc.open !== null)
286
+ arcBits.unshift(`open ${readiness.arc.open}`);
287
+ if (readiness.arc.fence)
288
+ arcBits.push(`fence ${shortCommitHash(readiness.arc.fence)}`);
289
+ if (readiness.arc.looseCommits.length > 0)
290
+ arcBits.push(`${readiness.arc.looseCommits.length} loose`);
291
+ lines.push(` arc ${arcBits.join(" · ")}`);
292
+ lines.push(` projection ${readiness.rewriteGuard.liveProjection ? "active" : "none"}`);
293
+ if (readiness.dependencies.length === 0) {
294
+ lines.push(" dependencies none");
295
+ }
296
+ else {
297
+ const deps = readiness.dependencies
298
+ .map((item) => `${item.id} ${item.satisfied ? "settled" : item.state}`)
299
+ .join(" · ");
300
+ lines.push(` dependencies ${deps}`);
301
+ }
302
+ if (readiness.petition.queue) {
303
+ const position = readiness.petition.queue.position ?? "?";
304
+ lines.push(` petition ${readiness.petition.state} · queue ${position}/${readiness.petition.queue.length}`);
305
+ }
306
+ else {
307
+ lines.push(` petition ${readiness.petition.state}`);
308
+ }
309
+ return lines;
310
+ }
311
+ function formatRaceValue(value) {
312
+ if (typeof value === "string") {
313
+ return /^[0-9a-f]{40}$/i.test(value) ? shortCommitHash(value) : value;
314
+ }
315
+ if (value === null || value === undefined)
316
+ return String(value);
317
+ if (typeof value !== "object" || Array.isArray(value))
318
+ return JSON.stringify(value);
319
+ const record = value;
320
+ const preferred = ["cwd", "ref", "head", "tree", "residueFingerprint"];
321
+ const parts = [];
322
+ for (const key of preferred) {
323
+ if (!(key in record))
324
+ continue;
325
+ const raw = record[key];
326
+ if (raw === undefined)
327
+ continue;
328
+ if (key === "head" || key === "tree" || key === "residueFingerprint") {
329
+ parts.push(`${key}=${typeof raw === "string" ? (key === "residueFingerprint" ? raw.slice(0, 12) : formatRaceValue(raw)) : JSON.stringify(raw)}`);
330
+ continue;
331
+ }
332
+ if (key === "ref" && typeof raw === "string") {
333
+ parts.push(`${key}=${shortRef(raw)}`);
334
+ continue;
335
+ }
336
+ parts.push(`${key}=${typeof raw === "string" ? raw : JSON.stringify(raw)}`);
337
+ }
338
+ if (parts.length === 0)
339
+ return JSON.stringify(value);
340
+ return parts.join(" · ");
57
341
  }
58
- function omissionLine(omission) {
59
- const subject = omission.hunk === null ? "file" : `hunk ${omission.hunk}`;
60
- return `omit ${subject} ${omission.path} · ${omission.bytes} bytes · ${omission.reason} · retrieve ${omission.retrieval.argv.map((part) => JSON.stringify(part)).join(" ")}`;
342
+ function renderCoordinateRace(finding) {
343
+ const lines = [` coordinate change observed`, ` ${finding.detail}`];
344
+ const race = finding.race;
345
+ if (!race)
346
+ return lines;
347
+ lines.push(` which ${race.which}`);
348
+ lines.push(` pinned ${formatRaceValue(race.pinned)}`);
349
+ lines.push(` observed ${formatRaceValue(race.observed)}`);
350
+ return lines;
61
351
  }
62
- function renderDiff(report) {
63
- if (!report.diff || !report.presentation)
64
- return ["not evaluated"];
65
- const selected = report.presentation.selected.map((item) => item.content);
66
- const omissions = report.presentation.omissions.map(omissionLine);
67
- const rows = report.files?.map((file) => `${file.status} ${file.oldPath ? `${file.oldPath} -> ` : ""}${file.path} · +${file.stats.insertions} -${file.stats.deletions} · ${file.class}`) ?? [];
68
- return [
69
- `${report.diff.stat.filesChanged} files · +${report.diff.stat.insertions} -${report.diff.stat.deletions}`,
70
- `budget ${report.presentation.budget.limit} bytes · used ${report.presentation.budget.used} bytes · omissions ${report.presentation.omissions.length}`,
71
- ...rows,
72
- ...selected,
73
- ...omissions,
74
- ];
352
+ function renderRehearsal(report) {
353
+ const rehearsal = report.rehearsal;
354
+ if (rehearsal.status === "fresh")
355
+ return "fresh";
356
+ if (rehearsal.status === "clean") {
357
+ return `clean onto ${shortRef(report.coordinates.target.ref)}@${shortCommitHash(rehearsal.newBase)}`;
358
+ }
359
+ if (rehearsal.status === "conflict") {
360
+ const paths = rehearsal.paths.length > 0 ? rehearsal.paths.join(", ") : "paths unavailable";
361
+ return `conflict · ${paths}`;
362
+ }
363
+ return `unknown · ${rehearsal.cause.code}: ${rehearsal.cause.message}`;
364
+ }
365
+ function renderCandidate(report) {
366
+ const target = `${shortRef(report.coordinates.target.ref)}@${shortCommitHash(report.coordinates.target.head)}`;
367
+ const delivery = `${shortRef(report.coordinates.delivery.ref)}@${shortCommitHash(report.coordinates.delivery.head)}`;
368
+ const lines = [];
369
+ if (report.candidate) {
370
+ const candidateHead = shortCommitHash(report.candidate.head);
371
+ const candidateBase = shortCommitHash(report.candidate.base);
372
+ if (report.candidate.kind === "renew-rehearsal") {
373
+ lines.push(` ${candidateBase}..${candidateHead} · renew rehearsal`);
374
+ lines.push(` delivery ${delivery}`);
375
+ lines.push(` target ${target}`);
376
+ }
377
+ else {
378
+ lines.push(` ${target}..${candidateHead}`);
379
+ if (report.candidate.head !== report.coordinates.delivery.head) {
380
+ lines.push(` delivery ${delivery}`);
381
+ }
382
+ }
383
+ }
384
+ else {
385
+ lines.push(` ${target}..${delivery}`);
386
+ }
387
+ if (report.diff) {
388
+ lines.push(` ${report.diff.stat.filesChanged} files · +${report.diff.stat.insertions} -${report.diff.stat.deletions}`);
389
+ }
390
+ else if (report.findings.some((finding) => finding.code === "COORDINATE_RACE")) {
391
+ lines.push(" evidence not evaluated · coordinate change");
392
+ }
393
+ else if (report.rehearsal.status === "conflict") {
394
+ lines.push(" evidence not evaluated · renew conflict");
395
+ }
396
+ else if (report.rehearsal.status === "unknown") {
397
+ lines.push(" evidence not evaluated · renew unknown");
398
+ }
399
+ lines.push(` rehearsal: ${renderRehearsal(report)}`);
400
+ lines.push(` ledger ${shortCommitHash(report.coordinates.ledger.head)}`);
401
+ return lines;
402
+ }
403
+ function renderAcceptance(report) {
404
+ if (report.terms.criteria.length === 0)
405
+ return [" (none)"];
406
+ return report.terms.criteria.map((term, index) => ` ${index + 1}. ${term}`);
75
407
  }
76
408
  export function buildAuditResponse(report, json) {
77
409
  const payload = report;
78
410
  if (json)
79
411
  return { text: JSON.stringify(report), payload };
80
- const text = [
412
+ const sections = [
81
413
  `Audit ${report.contract}`,
82
414
  "",
83
- "Coordinates",
84
- `ledger ${report.coordinates.ledger.head}`,
85
- `delivery ${report.coordinates.delivery.ref} ${report.coordinates.delivery.head} tree ${report.coordinates.delivery.tree}`,
86
- `target ${report.coordinates.target.ref} ${report.coordinates.target.head} tree ${report.coordinates.target.tree}`,
87
- "",
88
- "Criteria",
89
- ...report.terms.criteria.map((term) => `criterion ${term}`),
90
- ...report.terms.scope.map((term) => `scope ${term}`),
91
- ...report.terms.appendScope.map((term) => `append-scope ${term}`),
415
+ "Candidate",
416
+ ...renderCandidate(report),
92
417
  "",
93
- "Renew Rehearsal",
94
- JSON.stringify(report.rehearsal),
418
+ "Acceptance",
419
+ ...renderAcceptance(report),
95
420
  "",
96
- "Diff",
97
- ...renderDiff(report),
98
- `counts files=${report.counts.files} selected=${report.counts.selected} omitted=${report.counts.omitted} in-scope=${report.counts.inScope} downgraded=${report.counts.downgraded} violated=${report.counts.violated} findings=${report.counts.findings}`,
99
- "",
100
- "Scope",
101
- ...renderScope(report),
421
+ "Changes",
422
+ ...renderChanges(report),
102
423
  "",
103
424
  "Verification",
104
425
  ...renderVerification(report),
105
- "Readiness",
106
- ...renderReadiness(report),
107
426
  "",
108
- "Findings",
109
- ...(report.findings.length === 0 ? ["none"] : report.findings.flatMap((finding) => [`${finding.code}: ${finding.detail}`, `Next: ${finding.nextCommand}`])),
110
- ].join("\n");
111
- return { text, payload };
427
+ "Delivery Context",
428
+ ...renderDeliveryContext(report),
429
+ ];
430
+ return { text: sections.join("\n"), payload };
112
431
  }
113
432
  export function buildAuditConstructionFailure(error, json) {
114
433
  const cause = { code: error instanceof FlowError ? error.code : "INTERNAL_STATE", message: asMessage(error) };
@@ -117,6 +117,10 @@ function renderKeiyakuSection(board, width) {
117
117
  facts: contractFacts(contract),
118
118
  width,
119
119
  }));
120
+ if (contract.replayBlockers.length > 0) {
121
+ lines.push(` ! incomplete replay · ${contract.replayBlockers.map((blocker) => blocker.surface).join(", ")}`);
122
+ lines.push(` • ${contract.replayBlockers[0].recoveryCommand}`);
123
+ }
120
124
  }
121
125
  return lines.join("\n");
122
126
  }
@@ -231,13 +235,14 @@ function renderTaskSection(tasks, width) {
231
235
  lines.push(alignedBoardRow({
232
236
  prefix: "▶",
233
237
  title: taskLabel(task),
234
- facts: [{ text: `in_progress · active ${formatAgeMs(task.activityAgeMs) ?? "0s"}` }],
238
+ facts: [{ text: `${task.state === "on_hold" ? "on hold · updated" : "in_progress · active"} ${formatAgeMs(task.activityAgeMs) ?? "0s"}` }],
235
239
  width,
236
240
  }));
237
241
  }
238
242
  const folded = [
239
243
  tasks.ready > 0 ? `${tasks.ready} ready` : undefined,
240
244
  tasks.blocked > 0 ? `${tasks.blocked} blocked` : undefined,
245
+ tasks.onHold > 0 ? `${tasks.onHold} on hold` : undefined,
241
246
  ].filter((fact) => fact !== undefined);
242
247
  if (folded.length > 0)
243
248
  lines.push(tone(`+ ${folded.join(" · ")}`, "description"));
@@ -0,0 +1,56 @@
1
+ import { attachAddressCarrier } from "../../core/addressing.js";
2
+ import { isCompleteLifecycleOutcome } from "../../core/outcome-base.js";
3
+ import { FlowError } from "../../flow-error.js";
4
+ import { handleCliError } from "./errors.js";
5
+ import { coreTextResponse } from "./shared.js";
6
+ function isSuccessfulDerivedCleanup(result) {
7
+ return result.family === "precommit-effects-only"
8
+ && "status" in result
9
+ && result.status === "success"
10
+ && "cleanup" in result
11
+ && typeof result.cleanup === "object"
12
+ && result.cleanup !== null
13
+ && "status" in result.cleanup
14
+ && result.cleanup.status === "succeeded"
15
+ && [...result.prerequisites, ...result.downstream].every((row) => row.status !== "failed");
16
+ }
17
+ export async function buildLifecycleOutcomeResponse(result, context) {
18
+ if (result.family === "committed") {
19
+ return coreTextResponse(`lifecycle committed at ${result.committed.head}${result.replay.action === "reconcile" ? "; downstream reconciliation required" : ""}`, result);
20
+ }
21
+ const cause = result.family === "cancelled-before-effects"
22
+ ? result.cancellation
23
+ : result.family === "refused-before-effects"
24
+ ? result.failedInvariant
25
+ : [...result.prerequisites, ...result.downstream].find((row) => row.status === "failed")?.cause
26
+ ?? { code: "INTERNAL_STATE", message: "durable prerequisite effects require lifecycle reconciliation" };
27
+ if (!context) {
28
+ return { ...coreTextResponse(cause.message, result), exitCode: result.family === "cancelled-before-effects" ? 130 : 1 };
29
+ }
30
+ const facts = "facts" in cause ? cause.facts : undefined;
31
+ const error = new FlowError(cause.code, cause.message, {
32
+ ...(facts ? { facts: facts } : {}),
33
+ });
34
+ const rendered = await handleCliError({
35
+ error: context.address ? attachAddressCarrier(error, context.address) : error,
36
+ cwd: context.cwd,
37
+ logLabel: "lifecycle",
38
+ });
39
+ return {
40
+ ...rendered,
41
+ payload: result,
42
+ isDomainFailure: true,
43
+ exitCode: result.family === "cancelled-before-effects" ? 130 : 1,
44
+ };
45
+ }
46
+ export async function renderLifecycleResult(work, render, context) {
47
+ const value = await work;
48
+ if (isCompleteLifecycleOutcome(value))
49
+ return render(value);
50
+ if (isSuccessfulDerivedCleanup(value)) {
51
+ // Derived cleanup is a successful verb result, but deliberately is not a
52
+ // committed lifecycle result. The verb renderer owns that exact payload.
53
+ return render(value);
54
+ }
55
+ return buildLifecycleOutcomeResponse(value, context);
56
+ }