cool-workflow 0.1.98 → 0.2.0

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 (306) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/README.md +11 -2
  4. package/apps/architecture-review/app.json +1 -1
  5. package/apps/architecture-review-fast/app.json +1 -1
  6. package/apps/end-to-end-golden-path/app.json +1 -1
  7. package/apps/pr-review-fix-ci/app.json +1 -1
  8. package/apps/release-cut/app.json +1 -1
  9. package/apps/research-synthesis/app.json +1 -1
  10. package/dist/cli/dispatch.js +236 -0
  11. package/dist/cli/entry.js +120 -0
  12. package/dist/cli/io.js +21 -4
  13. package/dist/cli/parseargv.js +157 -0
  14. package/dist/cli.js +6 -33
  15. package/dist/core/capability-table.js +3534 -0
  16. package/dist/core/format/help.js +314 -0
  17. package/dist/{state-explosion/format.js → core/format/state-explosion-text.js} +10 -1
  18. package/dist/core/hash.js +137 -0
  19. package/dist/core/multi-agent/candidate-scoring.js +219 -0
  20. package/dist/core/multi-agent/collaboration.js +481 -0
  21. package/dist/core/multi-agent/coordinator.js +515 -0
  22. package/dist/core/multi-agent/eval-replay.js +306 -0
  23. package/dist/core/multi-agent/runtime.js +929 -0
  24. package/dist/core/multi-agent/topology.js +298 -0
  25. package/dist/core/multi-agent/trust-policy.js +197 -0
  26. package/dist/core/pipeline/commit-gate.js +320 -0
  27. package/dist/{pipeline-contract.js → core/pipeline/contract.js} +24 -37
  28. package/dist/core/pipeline/dispatch.js +103 -0
  29. package/dist/core/pipeline/drive-decide.js +227 -0
  30. package/dist/core/pipeline/error-feedback.js +161 -0
  31. package/dist/core/pipeline/loop-expansion.js +124 -0
  32. package/dist/{result-normalize.js → core/pipeline/result-normalize.js} +14 -37
  33. package/dist/core/pipeline/runner.js +230 -0
  34. package/dist/{contract-migration.js → core/state/contract-migration.js} +68 -92
  35. package/dist/{state-migrations.js → core/state/migrations.js} +103 -96
  36. package/dist/core/state/node-projection.js +95 -0
  37. package/dist/core/state/node-snapshot.js +230 -0
  38. package/dist/core/state/run-paths.js +93 -0
  39. package/dist/{schema-validate.js → core/state/schema-validate.js} +35 -28
  40. package/dist/core/state/schema.js +50 -0
  41. package/dist/core/state/state-explosion/digest.js +243 -0
  42. package/dist/core/state/state-explosion/graph.js +527 -0
  43. package/dist/{state-explosion → core/state/state-explosion}/helpers.js +34 -3
  44. package/dist/core/state/state-explosion/report.js +187 -0
  45. package/dist/{state-explosion → core/state/state-explosion}/size.js +25 -7
  46. package/dist/{state-node.js → core/state/state-node.js} +90 -113
  47. package/dist/core/state/types.js +19 -0
  48. package/dist/{validation.js → core/state/validation.js} +64 -131
  49. package/dist/core/trust/evidence-grounding.js +134 -0
  50. package/dist/core/trust/ledger.js +199 -0
  51. package/dist/{telemetry-attestation.js → core/trust/telemetry-attestation.js} +94 -68
  52. package/dist/core/trust/telemetry-ledger.js +127 -0
  53. package/dist/core/types.js +20 -0
  54. package/dist/core/version.js +16 -0
  55. package/dist/{workflow-app-framework.js → core/workflow-apps/app-schema.js} +337 -426
  56. package/dist/mcp/dispatch.js +104 -0
  57. package/dist/mcp/server.js +144 -0
  58. package/dist/mcp-server.js +6 -84
  59. package/dist/{agent-config.js → shell/agent-config.js} +118 -71
  60. package/dist/shell/app-run-cli.js +88 -0
  61. package/dist/shell/audit-cli.js +259 -0
  62. package/dist/shell/audit-provenance.js +83 -0
  63. package/dist/shell/candidate-scoring-io.js +459 -0
  64. package/dist/shell/collaboration-io.js +264 -0
  65. package/dist/shell/commit-summary.js +104 -0
  66. package/dist/shell/commit.js +290 -0
  67. package/dist/shell/coordinator-io.js +476 -0
  68. package/dist/shell/demo-cli.js +19 -0
  69. package/dist/shell/dispatch.js +162 -0
  70. package/dist/{doctor.js → shell/doctor.js} +123 -56
  71. package/dist/shell/drive.js +873 -0
  72. package/dist/shell/error-feedback-io.js +305 -0
  73. package/dist/shell/eval-io.js +473 -0
  74. package/dist/{multi-agent-eval/format.js → shell/eval-text.js} +78 -49
  75. package/dist/{evidence-reasoning.js → shell/evidence-reasoning.js} +124 -255
  76. package/dist/shell/exec-backend-cli.js +88 -0
  77. package/dist/shell/execution-backend/agent.js +475 -0
  78. package/dist/shell/execution-backend/ci.js +15 -0
  79. package/dist/shell/execution-backend/container.js +69 -0
  80. package/dist/shell/execution-backend/envelopes.js +55 -0
  81. package/dist/shell/execution-backend/local.js +113 -0
  82. package/dist/shell/execution-backend/probes.js +175 -0
  83. package/dist/shell/execution-backend/registry.js +402 -0
  84. package/dist/shell/execution-backend/remote.js +128 -0
  85. package/dist/shell/execution-backend/types.js +11 -0
  86. package/dist/shell/feedback-cli.js +81 -0
  87. package/dist/shell/feedback-operations.js +48 -0
  88. package/dist/shell/fs-atomic.js +276 -0
  89. package/dist/shell/harness.js +98 -0
  90. package/dist/shell/ledger-cli.js +212 -0
  91. package/dist/shell/ledger-io.js +169 -0
  92. package/dist/shell/man-cli.js +89 -0
  93. package/dist/shell/metrics-cli.js +98 -0
  94. package/dist/shell/multi-agent-cli.js +1002 -0
  95. package/dist/shell/multi-agent-host.js +563 -0
  96. package/dist/shell/multi-agent-io.js +387 -0
  97. package/dist/{multi-agent-operator-ux.js → shell/multi-agent-operator-ux.js} +234 -191
  98. package/dist/shell/node-store.js +124 -0
  99. package/dist/{observability/format.js → shell/observability-format.js} +7 -1
  100. package/dist/{observability/intake.js → shell/observability-intake.js} +79 -56
  101. package/dist/{observability.js → shell/observability.js} +159 -332
  102. package/dist/{onramp.js → shell/onramp.js} +11 -0
  103. package/dist/{operator-ux/format.js → shell/operator-ux-text.js} +250 -239
  104. package/dist/shell/operator-ux.js +431 -0
  105. package/dist/shell/orchestrator.js +231 -0
  106. package/dist/shell/pipeline-cli.js +667 -0
  107. package/dist/shell/pipeline.js +217 -0
  108. package/dist/shell/reclamation-io.js +1366 -0
  109. package/dist/shell/registry-cli.js +329 -0
  110. package/dist/{remote-source.js → shell/remote-source.js} +2 -2
  111. package/dist/shell/report-cli.js +101 -0
  112. package/dist/shell/report-view-cli.js +117 -0
  113. package/dist/{orchestrator → shell}/report.js +289 -282
  114. package/dist/shell/reporter.js +62 -0
  115. package/dist/shell/run-export-cli.js +106 -0
  116. package/dist/shell/run-export.js +680 -0
  117. package/dist/shell/run-registry-io.js +1014 -0
  118. package/dist/shell/run-store.js +164 -0
  119. package/dist/{sandbox-profile.js → shell/sandbox-profile.js} +134 -95
  120. package/dist/{scheduler.js → shell/scheduler-io.js} +248 -48
  121. package/dist/shell/scheduling-io.js +311 -0
  122. package/dist/shell/state-cli.js +181 -0
  123. package/dist/shell/state-explosion-cli.js +197 -0
  124. package/dist/shell/telemetry-cli.js +85 -0
  125. package/dist/{telemetry-demo.js → shell/telemetry-demo.js} +149 -119
  126. package/dist/shell/telemetry-ledger-io.js +132 -0
  127. package/dist/{term.js → shell/term.js} +36 -46
  128. package/dist/shell/topology-io.js +361 -0
  129. package/dist/shell/trust-audit.js +471 -0
  130. package/dist/{multi-agent-trust.js → shell/trust-policy-io.js} +67 -205
  131. package/dist/shell/verifier.js +48 -0
  132. package/dist/shell/workbench-host.js +250 -0
  133. package/dist/shell/workbench-text.js +18 -0
  134. package/dist/shell/workbench.js +175 -0
  135. package/dist/shell/worker-cli.js +124 -0
  136. package/dist/shell/worker-isolation.js +852 -0
  137. package/dist/shell/workflow-app-loader.js +650 -0
  138. package/docs/agent-delegation-drive.7.md +2 -0
  139. package/docs/cli-mcp-parity.7.md +280 -219
  140. package/docs/contract-migration-tooling.7.md +2 -0
  141. package/docs/control-plane-scheduling.7.md +2 -0
  142. package/docs/durable-state-and-locking.7.md +2 -0
  143. package/docs/evidence-adoption-reasoning-chain.7.md +2 -0
  144. package/docs/execution-backends.7.md +2 -0
  145. package/docs/multi-agent-cli-mcp-surface.7.md +2 -0
  146. package/docs/multi-agent-eval-replay-harness.7.md +2 -0
  147. package/docs/multi-agent-operator-ux.7.md +2 -0
  148. package/docs/node-snapshot-diff-replay.7.md +2 -0
  149. package/docs/observability-cost-accounting.7.md +2 -0
  150. package/docs/project-index.md +132 -71
  151. package/docs/real-execution-backends.7.md +2 -0
  152. package/docs/release-and-migration.7.md +2 -0
  153. package/docs/release-tooling.7.md +2 -0
  154. package/docs/run-registry-control-plane.7.md +2 -0
  155. package/docs/run-retention-reclamation.7.md +23 -0
  156. package/docs/state-explosion-management.7.md +2 -0
  157. package/docs/team-collaboration.7.md +2 -0
  158. package/docs/web-desktop-workbench.7.md +2 -0
  159. package/manifest/plugin.manifest.json +1 -1
  160. package/manifest/source-context-profiles.json +9 -13
  161. package/package.json +1 -1
  162. package/scripts/agents/cw-attest-wrap.js +2 -2
  163. package/scripts/bump-version.js +4 -3
  164. package/scripts/canonical-apps.js +4 -4
  165. package/scripts/dogfood-architecture-review.js +2 -3
  166. package/scripts/dogfood-release.js +3 -3
  167. package/scripts/gen-parity-doc.js +15 -2
  168. package/scripts/golden-path.js +4 -4
  169. package/scripts/onramp-check.js +1 -1
  170. package/scripts/parity-check.js +38 -21
  171. package/scripts/release-flow.js +2 -2
  172. package/scripts/sync-project-index.js +51 -27
  173. package/scripts/validate-run-state-schema.js +2 -2
  174. package/scripts/version-sync-check.js +30 -30
  175. package/dist/candidate-scoring.js +0 -729
  176. package/dist/capability-core.js +0 -1189
  177. package/dist/capability-registry.js +0 -885
  178. package/dist/cli/command-surface.js +0 -494
  179. package/dist/cli/format.js +0 -56
  180. package/dist/cli/handlers/audit.js +0 -82
  181. package/dist/cli/handlers/blackboard.js +0 -81
  182. package/dist/cli/handlers/candidate.js +0 -40
  183. package/dist/cli/handlers/clones.js +0 -34
  184. package/dist/cli/handlers/collaboration.js +0 -61
  185. package/dist/cli/handlers/eval.js +0 -40
  186. package/dist/cli/handlers/ledger.js +0 -169
  187. package/dist/cli/handlers/maintenance.js +0 -107
  188. package/dist/cli/handlers/multi-agent.js +0 -165
  189. package/dist/cli/handlers/node.js +0 -41
  190. package/dist/cli/handlers/operational.js +0 -155
  191. package/dist/cli/handlers/operator.js +0 -146
  192. package/dist/cli/handlers/registry.js +0 -68
  193. package/dist/cli/handlers/run.js +0 -153
  194. package/dist/cli/handlers/scheduling.js +0 -132
  195. package/dist/cli/handlers/workbench.js +0 -41
  196. package/dist/cli/handlers/worker.js +0 -45
  197. package/dist/cli/run-summary.js +0 -45
  198. package/dist/clones.js +0 -162
  199. package/dist/collaboration.js +0 -726
  200. package/dist/commit.js +0 -592
  201. package/dist/compare.js +0 -18
  202. package/dist/coordinator/classify.js +0 -45
  203. package/dist/coordinator/paths.js +0 -42
  204. package/dist/coordinator/util.js +0 -126
  205. package/dist/coordinator.js +0 -990
  206. package/dist/daemon.js +0 -44
  207. package/dist/dispatch.js +0 -250
  208. package/dist/drive.js +0 -864
  209. package/dist/error-feedback.js +0 -419
  210. package/dist/evidence-grounding.js +0 -184
  211. package/dist/execution-backend/agent.js +0 -354
  212. package/dist/execution-backend/probes.js +0 -112
  213. package/dist/execution-backend/util.js +0 -47
  214. package/dist/execution-backend.js +0 -961
  215. package/dist/gates.js +0 -48
  216. package/dist/harness.js +0 -61
  217. package/dist/ledger.js +0 -313
  218. package/dist/loop-expansion.js +0 -60
  219. package/dist/mcp/tool-call.js +0 -470
  220. package/dist/mcp/tool-definitions.js +0 -1066
  221. package/dist/mcp-surface.js +0 -30
  222. package/dist/multi-agent/graph.js +0 -84
  223. package/dist/multi-agent/helpers.js +0 -141
  224. package/dist/multi-agent/ids.js +0 -20
  225. package/dist/multi-agent/paths.js +0 -22
  226. package/dist/multi-agent-eval/normalize.js +0 -51
  227. package/dist/multi-agent-eval.js +0 -678
  228. package/dist/multi-agent-host.js +0 -777
  229. package/dist/multi-agent.js +0 -984
  230. package/dist/node-projection.js +0 -59
  231. package/dist/node-snapshot.js +0 -260
  232. package/dist/operator-ux.js +0 -631
  233. package/dist/orchestrator/app-operations.js +0 -211
  234. package/dist/orchestrator/audit-operations.js +0 -182
  235. package/dist/orchestrator/candidate-operations.js +0 -117
  236. package/dist/orchestrator/cli-options.js +0 -294
  237. package/dist/orchestrator/collaboration-operations.js +0 -86
  238. package/dist/orchestrator/feedback-operations.js +0 -81
  239. package/dist/orchestrator/host-operations.js +0 -78
  240. package/dist/orchestrator/lifecycle-operations.js +0 -650
  241. package/dist/orchestrator/migration-operations.js +0 -44
  242. package/dist/orchestrator/multi-agent-operations.js +0 -362
  243. package/dist/orchestrator/topology-operations.js +0 -84
  244. package/dist/orchestrator.js +0 -925
  245. package/dist/pipeline-runner.js +0 -285
  246. package/dist/reclamation/hash.js +0 -72
  247. package/dist/reclamation.js +0 -812
  248. package/dist/reporter.js +0 -67
  249. package/dist/run-export.js +0 -815
  250. package/dist/run-registry/derive.js +0 -175
  251. package/dist/run-registry/format.js +0 -124
  252. package/dist/run-registry/gc.js +0 -251
  253. package/dist/run-registry/policy.js +0 -16
  254. package/dist/run-registry/queue.js +0 -115
  255. package/dist/run-registry.js +0 -850
  256. package/dist/run-state-schema.js +0 -68
  257. package/dist/scheduling.js +0 -184
  258. package/dist/state-explosion.js +0 -1014
  259. package/dist/state.js +0 -367
  260. package/dist/telemetry-ledger.js +0 -196
  261. package/dist/topology.js +0 -565
  262. package/dist/triggers.js +0 -184
  263. package/dist/trust-audit.js +0 -644
  264. package/dist/types/blackboard.js +0 -2
  265. package/dist/types/candidate.js +0 -2
  266. package/dist/types/collaboration.js +0 -2
  267. package/dist/types/core.js +0 -2
  268. package/dist/types/drive.js +0 -10
  269. package/dist/types/error-feedback.js +0 -2
  270. package/dist/types/evidence-reasoning.js +0 -2
  271. package/dist/types/execution-backend.js +0 -2
  272. package/dist/types/multi-agent.js +0 -2
  273. package/dist/types/observability.js +0 -2
  274. package/dist/types/pipeline.js +0 -2
  275. package/dist/types/reclamation.js +0 -8
  276. package/dist/types/report-bundle.js +0 -6
  277. package/dist/types/result.js +0 -2
  278. package/dist/types/run-registry.js +0 -2
  279. package/dist/types/run.js +0 -2
  280. package/dist/types/sandbox.js +0 -2
  281. package/dist/types/schedule.js +0 -2
  282. package/dist/types/state-node.js +0 -2
  283. package/dist/types/topology.js +0 -2
  284. package/dist/types/trust.js +0 -2
  285. package/dist/types/workbench.js +0 -2
  286. package/dist/types/worker.js +0 -2
  287. package/dist/types/workflow-app.js +0 -2
  288. package/dist/types.js +0 -44
  289. package/dist/util/fingerprint.js +0 -19
  290. package/dist/util/fingerprint.test.js +0 -27
  291. package/dist/verifier.js +0 -78
  292. package/dist/version.js +0 -8
  293. package/dist/workbench-host.js +0 -192
  294. package/dist/workbench.js +0 -192
  295. package/dist/worker-accept/acceptance.js +0 -114
  296. package/dist/worker-accept/blackboard-fanout.js +0 -80
  297. package/dist/worker-accept/blackboard-linkage.js +0 -19
  298. package/dist/worker-accept/context.js +0 -2
  299. package/dist/worker-accept/telemetry-ledger.js +0 -126
  300. package/dist/worker-accept/validation.js +0 -77
  301. package/dist/worker-accept/verifier-completion.js +0 -73
  302. package/dist/worker-isolation/helpers.js +0 -51
  303. package/dist/worker-isolation/paths.js +0 -46
  304. package/dist/worker-isolation.js +0 -656
  305. package/dist/workflow-api.js +0 -131
  306. /package/dist/{types → core/types}/boundary.js +0 -0
@@ -1,218 +1,183 @@
1
1
  "use strict";
2
- // Report rendering for workflow runs extracted from orchestrator.ts.
2
+ // shell/report.ts report.md generation (the byte-exact section
3
+ // headers/fallback lines from the spec), the actual file write.
3
4
  //
4
- // Pure projection of a WorkflowRun into the human-readable run report and the
5
- // RunSummary view model. No orchestration state, no I/O beyond writing the
6
- // report file. The orchestrator re-imports the two public entry points
7
- // (writeReport, summarizeRun); every render*/format* helper here is private.
8
- var __importDefault = (this && this.__importDefault) || function (mod) {
9
- return (mod && mod.__esModule) ? mod : { "default": mod };
10
- };
5
+ // MILESTONE 6+7 (combined), EXTENDED at MILESTONE 11 (reporting/
6
+ // observability) with the four sections that were deferred while their
7
+ // own subsystems' milestones (8/9/4) had not yet landed: State Size &
8
+ // Compaction (state-explosion, milestone 4), Sandbox Profiles (milestone
9
+ // 5), Trust Audit (milestone 8), Acceptance Rationale (commit-gate,
10
+ // milestone 6+7). Byte-exact port of
11
+ // plugins/cool-workflow/src/orchestrator/report.ts's renderX helpers for
12
+ // each.
13
+ //
14
+ // Evidence: SPEC/reporting-ux.md "report.md (written by writeReport)";
15
+ // plugins/cool-workflow/src/orchestrator/report.ts:1-397 (byte-exact
16
+ // source).
17
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ var desc = Object.getOwnPropertyDescriptor(m, k);
20
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
21
+ desc = { enumerable: true, get: function() { return m[k]; } };
22
+ }
23
+ Object.defineProperty(o, k2, desc);
24
+ }) : (function(o, m, k, k2) {
25
+ if (k2 === undefined) k2 = k;
26
+ o[k2] = m[k];
27
+ }));
28
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
29
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
30
+ }) : function(o, v) {
31
+ o["default"] = v;
32
+ });
33
+ var __importStar = (this && this.__importStar) || (function () {
34
+ var ownKeys = function(o) {
35
+ ownKeys = Object.getOwnPropertyNames || function (o) {
36
+ var ar = [];
37
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
38
+ return ar;
39
+ };
40
+ return ownKeys(o);
41
+ };
42
+ return function (mod) {
43
+ if (mod && mod.__esModule) return mod;
44
+ var result = {};
45
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
46
+ __setModuleDefault(result, mod);
47
+ return result;
48
+ };
49
+ })();
11
50
  Object.defineProperty(exports, "__esModule", { value: true });
12
51
  exports.writeReport = writeReport;
13
- exports.summarizeRun = summarizeRun;
14
- const node_fs_1 = __importDefault(require("node:fs"));
15
- const dispatch_1 = require("../dispatch");
16
- const worker_isolation_1 = require("../worker-isolation");
17
- const candidate_scoring_1 = require("../candidate-scoring");
18
- const error_feedback_1 = require("../error-feedback");
19
- const multi_agent_1 = require("../multi-agent");
20
- const coordinator_1 = require("../coordinator");
21
- const trust_audit_1 = require("../trust-audit");
22
- const telemetry_ledger_1 = require("../telemetry-ledger");
23
- const state_explosion_1 = require("../state-explosion");
24
- function writeReport(run) {
25
- (0, dispatch_1.updatePhaseStatuses)(run);
26
- const workerSummary = (0, worker_isolation_1.summarizeWorkers)(run);
27
- const candidateSummary = (0, candidate_scoring_1.summarizeCandidates)(run);
28
- // A research run reads a local folder of files, not a code repo — label its source line
29
- // "Source". Skip the relabel when the run ALSO carries a remote-provenance "- Source: <url>"
30
- // line below (run.inputs.sourceUrl set by a --link/URL), so a report never shows two
31
- // "- Source:" lines. Every other app keeps the byte-identical "Repository:" (POLA).
32
- const sourceLabel = run.workflow.app?.metadata?.domain === "research" && !run.inputs.sourceUrl ? "Source" : "Repository";
33
- const report = [
34
- `# ${run.workflow.title}`,
35
- "",
36
- `- Run: ${run.id}`,
37
- `- Workflow: ${run.workflow.id}`,
38
- ...(run.workflow.app
39
- ? [
40
- `- Workflow App: ${run.workflow.app.id}@${run.workflow.app.version}`,
41
- `- Workflow App Source: ${run.workflow.app.source?.manifestPath || run.workflow.app.source?.entrypointPath || run.workflow.app.source?.path || ""}`
42
- ]
43
- : []),
44
- `- Created: ${run.createdAt}`,
45
- `- Updated: ${run.updatedAt}`,
46
- `- ${sourceLabel}: ${String(run.inputs.repo || run.cwd)}`,
47
- // Remote provenance (v0.1.91): when the repo was materialized from a --link/URL, record
48
- // the sanitized origin + resolved commit so the report itself says where the code came
49
- // from. Conditional — absent for a local-repo run, so existing reports stay byte-identical.
50
- ...(run.inputs.sourceUrl
51
- ? [`- Source: ${String(run.inputs.sourceUrl)}${run.inputs.sourceCommit ? `@${String(run.inputs.sourceCommit)}` : ""}`]
52
- : []),
53
- `- Question: ${String(run.inputs.question || "")}`,
54
- `- Invariants: ${formatInputList(run.inputs.invariant)}`,
55
- `- Loop Stage: ${run.loopStage}`,
56
- "",
57
- "## Phase Status",
58
- "",
59
- "| Phase | Status | Completed | Total |",
60
- "| --- | --- | ---: | ---: |",
61
- ...run.phases.map((phase) => {
62
- const phaseTasks = run.tasks.filter((task) => phase.taskIds.includes(task.id));
63
- const completed = phaseTasks.filter((task) => task.status === "completed").length;
64
- return `| ${phase.name} | ${phase.status} | ${completed} | ${phaseTasks.length} |`;
65
- }),
66
- "",
67
- "## State Commits",
68
- "",
69
- ...renderCommits(run),
70
- "",
71
- "## Error Feedback",
72
- "",
73
- ...renderFeedback(run),
74
- "",
75
- "## Workers",
76
- "",
77
- ...renderWorkers(workerSummary),
78
- "",
79
- "## State Size & Compaction",
80
- "",
81
- ...renderStateSize(run),
82
- "",
83
- "## Multi-Agent Runtime",
84
- "",
85
- ...renderMultiAgent(run),
86
- "",
87
- "## Blackboard / Coordinator",
88
- "",
89
- ...renderBlackboard(run),
90
- "",
91
- "## Sandbox Profiles",
92
- "",
93
- ...renderSandboxProfiles(run),
94
- "",
95
- "## Trust Audit",
96
- "",
97
- ...renderTrustAudit(run),
98
- "",
99
- "## Acceptance Rationale",
100
- "",
101
- ...renderAcceptanceRationale(run),
102
- "",
103
- "## Candidates",
104
- "",
105
- ...renderCandidates(candidateSummary),
106
- "",
107
- "## Pending Tasks",
108
- "",
109
- ...renderPendingTasks(run),
110
- "",
111
- "## Results",
112
- "",
113
- ...renderResults(run)
114
- ].join("\n");
115
- node_fs_1.default.writeFileSync(run.paths.report, report, "utf8");
116
- return run.paths.report;
117
- }
118
- function summarizeRun(run) {
119
- (0, dispatch_1.updatePhaseStatuses)(run);
120
- const workerSummary = (0, worker_isolation_1.summarizeWorkers)(run);
121
- const createdAtMs = Date.parse(run.createdAt);
122
- const updatedAtMs = Date.parse(run.updatedAt);
123
- const durationMs = Number.isFinite(createdAtMs) && Number.isFinite(updatedAtMs) ? Math.max(0, updatedAtMs - createdAtMs) : undefined;
124
- return {
125
- runId: run.id,
126
- workflowId: run.workflow.id,
127
- app: run.workflow.app,
128
- phases: run.phases,
129
- tasks: {
130
- total: run.tasks.length,
131
- pending: run.tasks.filter((task) => task.status === "pending").length,
132
- running: run.tasks.filter((task) => task.status === "running").length,
133
- failed: run.tasks.filter((task) => task.status === "failed").length,
134
- completed: run.tasks.filter((task) => task.status === "completed").length
135
- },
136
- loopStage: run.loopStage,
137
- durationMs,
138
- progressPercent: run.tasks.length ? Math.round((run.tasks.filter((t) => t.status === "completed").length / run.tasks.length) * 100) : 0,
139
- next: (0, dispatch_1.firstRunnablePhase)(run)?.name || null,
140
- reportPath: run.paths.report,
141
- commits: run.commits,
142
- workers: {
143
- total: workerSummary.total,
144
- byStatus: workerSummary.byStatus
145
- }
146
- };
52
+ const fs = __importStar(require("node:fs"));
53
+ const dispatch_1 = require("../core/pipeline/dispatch");
54
+ const state_explosion_cli_1 = require("./state-explosion-cli");
55
+ const state_explosion_text_1 = require("../core/format/state-explosion-text");
56
+ const candidate_scoring_io_1 = require("./candidate-scoring-io");
57
+ const trust_audit_1 = require("./trust-audit");
58
+ const telemetry_ledger_io_1 = require("./telemetry-ledger-io");
59
+ const multi_agent_io_1 = require("./multi-agent-io");
60
+ const coordinator_io_1 = require("./coordinator-io");
61
+ const multi_agent_operator_ux_1 = require("./multi-agent-operator-ux");
62
+ function formatInputList(value) {
63
+ if (Array.isArray(value))
64
+ return value.join("; ");
65
+ return value ? String(value) : "";
147
66
  }
148
- function renderPendingTasks(run) {
149
- const pending = run.tasks.filter((task) => task.status === "pending" || task.status === "running");
150
- if (!pending.length)
151
- return ["No pending tasks."];
152
- return pending.map((task) => `- ${task.id} (${task.phase}, ${task.status}): ${task.taskPath}`);
67
+ function formatCounts(counts) {
68
+ const entries = Object.entries(counts).sort(([a], [b]) => a.localeCompare(b));
69
+ if (!entries.length)
70
+ return "none";
71
+ return entries.map(([k, v]) => `${k}=${v}`).join(", ");
153
72
  }
154
- function renderResults(run) {
155
- const completed = run.tasks.filter((task) => task.status === "completed");
156
- if (!completed.length)
157
- return ["No completed results yet."];
158
- const lines = [];
159
- for (const task of completed) {
160
- lines.push(`### ${task.id}`, "", `Result: ${task.resultPath}`, "");
161
- if (task.resultPath && node_fs_1.default.existsSync(task.resultPath)) {
162
- lines.push(node_fs_1.default.readFileSync(task.resultPath, "utf8").trim(), "");
163
- }
164
- else {
165
- lines.push("_Result file is not present on this host; state metadata remains inspectable._", "");
166
- }
73
+ function countBy(values, key) {
74
+ const counts = {};
75
+ for (const value of values) {
76
+ const bucket = key(value);
77
+ counts[bucket] = (counts[bucket] || 0) + 1;
167
78
  }
168
- return lines;
79
+ return counts;
169
80
  }
170
81
  function renderCommits(run) {
171
82
  if (!run.commits.length)
172
83
  return ["No state commits yet."];
173
84
  return run.commits.map((commit) => {
174
85
  const kind = commit.verifierGated ? "verifier-gated commit" : "checkpoint";
175
- const gate = commit.verifierGated ? formatCommitGate(commit) : "verifierGated=false";
86
+ const gate = commit.verifierGated ? `verifier=${commit.verifierNodeId || "unknown"}, evidence=${commit.evidence?.length || 0}` : "verifierGated=false";
176
87
  return `- ${commit.id}: ${commit.reason} [${commit.loopStage}; ${kind}; ${gate}] (${commit.snapshotPath})`;
177
88
  });
178
89
  }
179
- function renderFeedback(run) {
180
- const summary = (0, error_feedback_1.summarizeFeedback)(run);
181
- if (!summary.total)
182
- return ["No feedback records."];
90
+ /** `## State Size & Compaction` — milestone 4's state-explosion report,
91
+ * rendered with its own dedicated text formatter (always non-empty; no
92
+ * "no records" fallback in the old build either). */
93
+ function renderStateSize(run) {
94
+ const index = (0, state_explosion_cli_1.loadStateExplosionSummaryIndex)(run);
95
+ const report = (0, state_explosion_cli_1.buildStateExplosionReport)(run, { index, operator: (0, multi_agent_operator_ux_1.operatorDigestInput)(run) });
96
+ return (0, state_explosion_text_1.stateExplosionReportLines)(report);
97
+ }
98
+ /** `## Sandbox Profiles` — byte-exact port of the old build's
99
+ * renderSandboxProfiles (src/orchestrator/report.ts:277-287). */
100
+ function renderSandboxProfiles(run) {
101
+ const profiles = run.sandboxProfiles || [];
102
+ if (!profiles.length)
103
+ return ["No sandbox profiles selected yet."];
104
+ return profiles.map((profile) => [
105
+ `- ${profile.id}: read=${profile.readPaths.length}, write=${profile.writePaths.length}, execute=${profile.execute.mode}, network=${profile.network.mode}`,
106
+ ` enforcedByCW=${profile.enforcement.enforcedByCW.join("; ")}`,
107
+ ` hostRequired=${profile.enforcement.hostRequired.join("; ")}`,
108
+ ].join("\n"));
109
+ }
110
+ /** `## Trust Audit` — byte-exact port of the old build's renderTrustAudit
111
+ * + renderTelemetryAttestation (src/orchestrator/report.ts:301-355). */
112
+ function renderTrustAudit(run) {
113
+ const summary = (0, trust_audit_1.summarizeTrustAudit)(run);
114
+ const integrity = summary.integrity;
183
115
  return [
184
- `- Total: ${summary.total}`,
185
- `- By status: ${formatCounts(summary.byStatus)}`,
186
- `- By severity: ${formatCounts(summary.bySeverity)}`,
187
- `- By classification: ${formatCounts(summary.byClassification)}`,
188
- "",
189
- ...summary.artifacts.map((artifact) => `- ${artifact}`)
116
+ `- Events: ${summary.eventCount}`,
117
+ `- Chain integrity: ${integrity ? (integrity.verified ? "verified" : "FAILED") : "n/a"}` +
118
+ `${integrity ? ` (${integrity.chained} chained, ${integrity.unchained} legacy${integrity.corruptLines ? `, ${integrity.corruptLines} corrupt` : ""})` : ""}`,
119
+ ...(integrity && !integrity.verified
120
+ ? [` !! TRUST-AUDIT CHAIN TAMPER DETECTED: ${integrity.checks.filter((c) => !c.pass).map((c) => c.code).join(", ")}`]
121
+ : []),
122
+ `- Decisions: ${formatCounts(summary.byDecision)}`,
123
+ `- Sources: ${formatCounts(summary.bySource)}`,
124
+ `- Sandbox profiles: ${formatCounts(summary.bySandboxProfile)}`,
125
+ `- Event log: ${summary.eventLogPath}`,
126
+ `- Summary: ${summary.summaryPath}`,
127
+ `- Index: ${summary.indexPath}`,
128
+ ...renderTelemetryAttestation(run),
190
129
  ];
191
130
  }
192
- function renderWorkers(summary) {
193
- if (!summary.total)
194
- return ["No worker scopes yet."];
131
+ /** Telemetry attestation coverage + a LOUD list of any `unattested`
132
+ * usage, byte-exact port of the old build's renderTelemetryAttestation. */
133
+ function renderTelemetryAttestation(run) {
134
+ const delegations = (0, trust_audit_1.listTrustAuditEvents)(run).filter((event) => event.kind === "worker.agent-delegation" && event.metadata && event.metadata.telemetryAttestation);
135
+ if (!delegations.length)
136
+ return [];
137
+ const statusOf = (event) => String(event.metadata.telemetryAttestation);
138
+ const attested = delegations.filter((event) => statusOf(event) === "attested").length;
139
+ const unattested = delegations.filter((event) => statusOf(event) === "unattested");
140
+ const absent = delegations.filter((event) => statusOf(event) === "absent").length;
195
141
  const lines = [
196
- `- Total: ${summary.total}`,
197
- `- By status: ${formatCounts(summary.byStatus)}`,
198
- "",
199
- ...summary.manifestPaths.map((artifact) => `- ${artifact}`)
142
+ `- Telemetry attestation: ${attested}/${delegations.length} attested` +
143
+ (unattested.length ? `, ${unattested.length} UNATTESTED` : "") +
144
+ (absent ? `, ${absent} absent` : ""),
200
145
  ];
201
- if (summary.failed.length) {
202
- lines.push("", "Failed or rejected:");
203
- for (const worker of summary.failed) {
204
- lines.push(`- ${worker.id} (${worker.status}) feedback=${worker.feedbackIds.join(",") || "none"}`);
205
- }
146
+ for (const event of unattested) {
147
+ const reason = event.metadata.telemetryAttestationReason;
148
+ lines.push(` - ⚠️ UNATTESTED usage — worker=${event.workerId || "?"} task=${event.taskId || "?"}: ${reason || "signature unverified"}`);
149
+ }
150
+ const ledger = (0, telemetry_ledger_io_1.verifyTelemetryLedger)(run);
151
+ if (ledger.present) {
152
+ lines.push(ledger.verified
153
+ ? `- Attestation ledger: ${ledger.records.length} records, chain verified (tamper-evident)`
154
+ : ` - ⚠️ ATTESTATION LEDGER CHAIN BROKEN — a recorded verdict/usage was edited after the fact (${ledger.checks.filter((c) => !c.pass).map((c) => c.name).join(", ")})`);
206
155
  }
207
156
  return lines;
208
157
  }
209
- function renderStateSize(run) {
210
- const index = (0, state_explosion_1.loadStateExplosionSummaryIndex)(run);
211
- const report = (0, state_explosion_1.buildStateExplosionReport)(run, { index });
212
- return (0, state_explosion_1.stateExplosionReportLines)(report);
158
+ /** `## Acceptance Rationale` — byte-exact port of the old build's
159
+ * renderAcceptanceRationale (src/orchestrator/report.ts:357-374). */
160
+ function renderAcceptanceRationale(run) {
161
+ const lines = [];
162
+ for (const selectionRaw of run.candidateSelections || []) {
163
+ const selection = selectionRaw;
164
+ const rationale = selection.acceptanceRationale;
165
+ if (!rationale)
166
+ continue;
167
+ lines.push(`- Selection ${selection.id}: candidate=${rationale.selectedCandidateId || selection.candidateId}, score=${rationale.scoreId || "none"}, verifier=${rationale.verifierNodeId || "none"}, evidence=${rationale.evidenceCount}, sandbox=${rationale.sandboxProfileId || "none"}, worker=${rationale.workerId || "none"}`);
168
+ }
169
+ for (const commit of run.commits || []) {
170
+ if (!commit.acceptanceRationale)
171
+ continue;
172
+ const rationale = commit.acceptanceRationale;
173
+ lines.push(`- Commit ${commit.id}: gate=${rationale.commitGateResult || "unknown"}, candidate=${rationale.selectedCandidateId || commit.candidateId || "none"}, score=${rationale.scoreId || "none"}, verifier=${rationale.verifierNodeId || commit.verifierNodeId || "none"}, evidence=${rationale.evidenceCount}, sandbox=${rationale.sandboxProfileId || "none"}, worker=${rationale.workerId || "none"}`);
174
+ }
175
+ return lines.length ? lines : ["No accepted candidate or verifier-gated commit rationale yet."];
213
176
  }
177
+ /** `## Multi-Agent Runtime` — byte-exact port of the old build's
178
+ * renderMultiAgent (src/orchestrator/report.ts:217-242). */
214
179
  function renderMultiAgent(run) {
215
- const summary = (0, multi_agent_1.summarizeMultiAgent)(run);
180
+ const summary = (0, multi_agent_io_1.summarizeMultiAgent)(run);
216
181
  if (!summary.totalRuns)
217
182
  return ["No multi-agent runtime records yet."];
218
183
  const lines = [
@@ -221,7 +186,7 @@ function renderMultiAgent(run) {
221
186
  `- Groups: ${summary.groups} (${formatCounts(summary.groupsByStatus)})`,
222
187
  `- Memberships: ${summary.memberships} (${formatCounts(summary.membershipsByStatus)})`,
223
188
  `- Fanouts: ${summary.fanouts}`,
224
- `- Fanins: ${summary.fanins} (${formatCounts(summary.faninsByStatus)})`
189
+ `- Fanins: ${summary.fanins} (${formatCounts(summary.faninsByStatus)})`,
225
190
  ];
226
191
  if (summary.blockedReasons.length) {
227
192
  lines.push("", "Blocked:");
@@ -240,8 +205,10 @@ function renderMultiAgent(run) {
240
205
  lines.push("", `Next multi-agent action: ${summary.nextAction}`);
241
206
  return lines;
242
207
  }
208
+ /** `## Blackboard / Coordinator` — byte-exact port of the old build's
209
+ * renderBlackboard (src/orchestrator/report.ts:244-275). */
243
210
  function renderBlackboard(run) {
244
- const summary = (0, coordinator_1.summarizeBlackboard)(run);
211
+ const summary = (0, coordinator_io_1.summarizeBlackboard)(run);
245
212
  if (!summary.blackboardId)
246
213
  return ["No blackboard records yet."];
247
214
  const lines = [
@@ -254,7 +221,7 @@ function renderBlackboard(run) {
254
221
  `- Decisions: ${summary.decisions}`,
255
222
  `- Ready for fanin: ${summary.readyForFanin ? "yes" : "no"}`,
256
223
  `- Index: ${summary.indexPath || "none"}`,
257
- `- Latest snapshot: ${summary.latestSnapshotPath || "none"}`
224
+ `- Latest snapshot: ${summary.latestSnapshotPath || "none"}`,
258
225
  ];
259
226
  if (summary.openQuestions.length) {
260
227
  lines.push("", "Open questions:");
@@ -276,17 +243,10 @@ function renderBlackboard(run) {
276
243
  lines.push("", `Next coordinator action: ${summary.nextAction}`);
277
244
  return lines;
278
245
  }
279
- function renderSandboxProfiles(run) {
280
- const profiles = run.sandboxProfiles || [];
281
- if (!profiles.length)
282
- return ["No sandbox profiles selected yet."];
283
- return profiles.map((profile) => [
284
- `- ${profile.id}: read=${profile.readPaths.length}, write=${profile.writePaths.length}, execute=${profile.execute.mode}, network=${profile.network.mode}`,
285
- ` enforcedByCW=${profile.enforcement.enforcedByCW.join("; ")}`,
286
- ` hostRequired=${profile.enforcement.hostRequired.join("; ")}`
287
- ].join("\n"));
288
- }
289
- function renderCandidates(summary) {
246
+ /** `## Candidates` — byte-exact port of the old build's renderCandidates
247
+ * (src/orchestrator/report.ts:289-299). */
248
+ function renderCandidatesSection(run) {
249
+ const summary = (0, candidate_scoring_io_1.summarizeCandidates)(run);
290
250
  if (!summary.total)
291
251
  return ["No candidates yet."];
292
252
  return [
@@ -295,94 +255,141 @@ function renderCandidates(summary) {
295
255
  `- By kind: ${formatCounts(summary.byKind)}`,
296
256
  `- Selections: ${summary.selections}`,
297
257
  `- Index: ${summary.indexPath}`,
298
- `- Ranking: ${summary.rankingPath}`
258
+ `- Ranking: ${summary.rankingPath}`,
299
259
  ];
300
260
  }
301
- function renderTrustAudit(run) {
302
- const summary = (0, trust_audit_1.summarizeTrustAudit)(run);
303
- const integrity = summary.integrity;
261
+ function renderFeedback(run) {
262
+ const records = (run.feedback || []);
263
+ if (!records.length)
264
+ return ["No feedback records."];
304
265
  return [
305
- `- Events: ${summary.eventCount}`,
306
- `- Chain integrity: ${integrity ? (integrity.verified ? "verified" : "FAILED") : "n/a"}` +
307
- `${integrity ? ` (${integrity.chained} chained, ${integrity.unchained} legacy${integrity.corruptLines ? `, ${integrity.corruptLines} corrupt` : ""})` : ""}`,
308
- // An auditable control-plane never lets a broken decision-log chain pass
309
- // silently — name the failing checks loudly, same as the telemetry chain.
310
- ...(integrity && !integrity.verified
311
- ? [` !! TRUST-AUDIT CHAIN TAMPER DETECTED: ${integrity.checks.filter((c) => !c.pass).map((c) => c.code).join(", ")}`]
312
- : []),
313
- `- Decisions: ${formatCounts(summary.byDecision)}`,
314
- `- Sources: ${formatCounts(summary.bySource)}`,
315
- `- Sandbox profiles: ${formatCounts(summary.bySandboxProfile)}`,
316
- `- Event log: ${summary.eventLogPath}`,
317
- `- Summary: ${summary.summaryPath}`,
318
- `- Index: ${summary.indexPath}`,
319
- ...renderTelemetryAttestation(run)
266
+ `- Total: ${records.length}`,
267
+ `- By status: ${formatCounts(countBy(records, (r) => r.status))}`,
268
+ `- By severity: ${formatCounts(countBy(records, (r) => r.severity))}`,
269
+ `- By classification: ${formatCounts(countBy(records, (r) => r.classification))}`,
320
270
  ];
321
271
  }
322
- /** Track 1: telemetry attestation coverage + a LOUD list of any `unattested`
323
- * usage. An auditable control-plane never lets unverified telemetry pass
324
- * silently — every reported-but-unverified usage is named here with its reason. */
325
- function renderTelemetryAttestation(run) {
326
- const delegations = (0, trust_audit_1.listTrustAuditEvents)(run).filter((event) => event.kind === "worker.agent-delegation" && event.metadata && event.metadata.telemetryAttestation);
327
- if (!delegations.length)
328
- return [];
329
- const statusOf = (event) => String(event.metadata.telemetryAttestation);
330
- const attested = delegations.filter((event) => statusOf(event) === "attested").length;
331
- const unattested = delegations.filter((event) => statusOf(event) === "unattested");
332
- const absent = delegations.filter((event) => statusOf(event) === "absent").length;
333
- const lines = [
334
- `- Telemetry attestation: ${attested}/${delegations.length} attested` +
335
- (unattested.length ? `, ${unattested.length} UNATTESTED` : "") +
336
- (absent ? `, ${absent} absent` : "")
337
- ];
338
- for (const event of unattested) {
339
- const reason = event.metadata.telemetryAttestationReason;
340
- lines.push(` - ⚠️ UNATTESTED usage — worker=${event.workerId || "?"} task=${event.taskId || "?"}: ${reason || "signature unverified"}`);
341
- }
342
- // Tamper-evidence: re-prove the hash-chained ledger. A broken chain means a
343
- // recorded verdict/usage was edited after the fact — surfaced LOUDLY.
344
- const ledger = (0, telemetry_ledger_1.verifyTelemetryLedger)(run);
345
- if (ledger.present) {
346
- lines.push(ledger.verified
347
- ? `- Attestation ledger: ${ledger.records.length} records, chain verified (tamper-evident)`
348
- : ` - ⚠️ ATTESTATION LEDGER CHAIN BROKEN — a recorded verdict/usage was edited after the fact (${ledger.checks.filter((c) => !c.pass).map((c) => c.name).join(", ")})`);
272
+ function renderWorkers(run) {
273
+ const workers = run.workers || [];
274
+ if (!workers.length)
275
+ return ["No worker scopes yet."];
276
+ const lines = [`- Total: ${workers.length}`, `- By status: ${formatCounts(countBy(workers, (w) => w.status))}`];
277
+ const failed = workers.filter((w) => w.status === "failed" || w.status === "rejected");
278
+ if (failed.length) {
279
+ lines.push("", "Failed or rejected:");
280
+ for (const w of failed)
281
+ lines.push(`- ${w.id} (${w.status}) feedback=${w.feedbackIds.join(",") || "none"}`);
349
282
  }
350
283
  return lines;
351
284
  }
352
- function renderAcceptanceRationale(run) {
285
+ function renderPendingTasks(run) {
286
+ const pending = run.tasks.filter((t) => t.status === "pending" || t.status === "running");
287
+ if (!pending.length)
288
+ return ["No pending tasks."];
289
+ return pending.map((t) => `- ${t.id} (${t.phase}, ${t.status}): ${t.taskPath}`);
290
+ }
291
+ function renderResults(run) {
292
+ const completed = run.tasks.filter((t) => t.status === "completed");
293
+ if (!completed.length)
294
+ return ["No completed results yet."];
353
295
  const lines = [];
354
- for (const selection of run.candidateSelections || []) {
355
- const rationale = selection.acceptanceRationale;
356
- if (!rationale)
357
- continue;
358
- lines.push(`- Selection ${selection.id}: candidate=${rationale.selectedCandidateId || selection.candidateId}, score=${rationale.scoreId || "none"}, verifier=${rationale.verifierNodeId || "none"}, evidence=${rationale.evidenceCount}, sandbox=${rationale.sandboxProfileId || "none"}, worker=${rationale.workerId || "none"}`);
359
- }
360
- for (const commit of run.commits || []) {
361
- if (!commit.acceptanceRationale)
362
- continue;
363
- const rationale = commit.acceptanceRationale;
364
- lines.push(`- Commit ${commit.id}: gate=${rationale.commitGateResult || "unknown"}, candidate=${rationale.selectedCandidateId || commit.candidateId || "none"}, score=${rationale.scoreId || "none"}, verifier=${rationale.verifierNodeId || commit.verifierNodeId || "none"}, evidence=${rationale.evidenceCount}, sandbox=${rationale.sandboxProfileId || "none"}, worker=${rationale.workerId || "none"}`);
296
+ for (const task of completed) {
297
+ lines.push(`### ${task.id}`, "", `Result: ${task.resultPath}`, "");
298
+ if (task.resultPath && fs.existsSync(task.resultPath)) {
299
+ lines.push(fs.readFileSync(task.resultPath, "utf8").trim(), "");
300
+ }
301
+ else {
302
+ lines.push("_Result file is not present on this host; state metadata remains inspectable._", "");
303
+ }
365
304
  }
366
- return lines.length ? lines : ["No accepted candidate or verifier-gated commit rationale yet."];
367
- }
368
- function formatCommitGate(commit) {
369
- return [
370
- `verifier=${commit.verifierNodeId || "unknown"}`,
371
- commit.candidateId ? `candidate=${commit.candidateId}` : "",
372
- commit.selectionId ? `selection=${commit.selectionId}` : "",
373
- `evidence=${commit.evidence?.length || 0}`
374
- ]
375
- .filter(Boolean)
376
- .join(", ");
377
- }
378
- function formatCounts(counts) {
379
- const entries = Object.entries(counts).sort(([left], [right]) => left.localeCompare(right));
380
- if (!entries.length)
381
- return "none";
382
- return entries.map(([key, value]) => `${key}=${value}`).join(", ");
305
+ return lines;
383
306
  }
384
- function formatInputList(value) {
385
- if (Array.isArray(value))
386
- return value.join("; ");
387
- return value ? String(value) : "";
307
+ /** writeReport — renders report.md and writes it. Returns the path. */
308
+ function writeReport(run) {
309
+ (0, dispatch_1.updatePhaseStatuses)(run);
310
+ const workflowApp = run.workflow.app;
311
+ // A research run reads a local folder of files, not a code repo — label
312
+ // its source line "Source". Skip the relabel when the run ALSO carries a
313
+ // remote-provenance "- Source: <url>" line below (run.inputs.sourceUrl
314
+ // set by a --link/URL), so a report never shows two "- Source:" lines.
315
+ // Every other app keeps the byte-identical "Repository:" (POLA).
316
+ const sourceLabel = workflowApp?.metadata?.domain === "research" && !run.inputs.sourceUrl ? "Source" : "Repository";
317
+ const report = [
318
+ `# ${run.workflow.title}`,
319
+ "",
320
+ `- Run: ${run.id}`,
321
+ `- Workflow: ${run.workflow.id}`,
322
+ ...(workflowApp
323
+ ? [`- Workflow App: ${workflowApp.id}@${workflowApp.version}`, `- Workflow App Source: ${workflowApp.source?.manifestPath || workflowApp.source?.entrypointPath || workflowApp.source?.path || ""}`]
324
+ : []),
325
+ `- Created: ${run.createdAt}`,
326
+ `- Updated: ${run.updatedAt}`,
327
+ `- ${sourceLabel}: ${String(run.inputs.repo || run.cwd)}`,
328
+ ...(run.inputs.sourceUrl
329
+ ? [`- Source: ${String(run.inputs.sourceUrl)}${run.inputs.sourceCommit ? `@${String(run.inputs.sourceCommit)}` : ""}`]
330
+ : []),
331
+ `- Question: ${String(run.inputs.question || "")}`,
332
+ `- Invariants: ${formatInputList(run.inputs.invariant)}`,
333
+ `- Loop Stage: ${run.loopStage}`,
334
+ "",
335
+ "## Phase Status",
336
+ "",
337
+ "| Phase | Status | Completed | Total |",
338
+ "| --- | --- | ---: | ---: |",
339
+ ...run.phases.map((phase) => {
340
+ const phaseTasks = run.tasks.filter((t) => phase.taskIds.includes(t.id));
341
+ const completed = phaseTasks.filter((t) => t.status === "completed").length;
342
+ return `| ${phase.name} | ${phase.status} | ${completed} | ${phaseTasks.length} |`;
343
+ }),
344
+ "",
345
+ "## State Commits",
346
+ "",
347
+ ...renderCommits(run),
348
+ "",
349
+ "## Error Feedback",
350
+ "",
351
+ ...renderFeedback(run),
352
+ "",
353
+ "## Workers",
354
+ "",
355
+ ...renderWorkers(run),
356
+ "",
357
+ "## State Size & Compaction",
358
+ "",
359
+ ...renderStateSize(run),
360
+ "",
361
+ "## Multi-Agent Runtime",
362
+ "",
363
+ ...renderMultiAgent(run),
364
+ "",
365
+ "## Blackboard / Coordinator",
366
+ "",
367
+ ...renderBlackboard(run),
368
+ "",
369
+ "## Sandbox Profiles",
370
+ "",
371
+ ...renderSandboxProfiles(run),
372
+ "",
373
+ "## Trust Audit",
374
+ "",
375
+ ...renderTrustAudit(run),
376
+ "",
377
+ "## Acceptance Rationale",
378
+ "",
379
+ ...renderAcceptanceRationale(run),
380
+ "",
381
+ "## Candidates",
382
+ "",
383
+ ...renderCandidatesSection(run),
384
+ "",
385
+ "## Pending Tasks",
386
+ "",
387
+ ...renderPendingTasks(run),
388
+ "",
389
+ "## Results",
390
+ "",
391
+ ...renderResults(run),
392
+ ].join("\n");
393
+ fs.writeFileSync(run.paths.report, report, "utf8");
394
+ return run.paths.report;
388
395
  }