@astrosheep/keiyaku 2.9.8 → 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 +10 -7
  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 +242 -79
  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 +65 -29
  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 +13 -35
  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 +45 -69
  87. package/build/cli/subagent-guard.js +1 -1
  88. package/build/cli/types.js +1 -1
  89. package/build/config/env-keys.js +3 -1
  90. package/build/config/env.js +24 -1
  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 +13 -6
  129. package/build/core/projection/index.js +5 -4
  130. package/build/core/projection/projection-activity.js +29 -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 +35 -337
  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 +125 -127
  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) };