cool-workflow 0.1.97 → 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 (309) 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} +12 -5
  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 +4 -0
  139. package/docs/cli-mcp-parity.7.md +284 -211
  140. package/docs/contract-migration-tooling.7.md +4 -0
  141. package/docs/control-plane-scheduling.7.md +4 -0
  142. package/docs/cross-agent-ledger.7.md +217 -0
  143. package/docs/designs/handoff-ledger.md +145 -0
  144. package/docs/durable-state-and-locking.7.md +4 -0
  145. package/docs/evidence-adoption-reasoning-chain.7.md +4 -0
  146. package/docs/execution-backends.7.md +4 -0
  147. package/docs/handoff-setup.md +120 -0
  148. package/docs/multi-agent-cli-mcp-surface.7.md +4 -0
  149. package/docs/multi-agent-eval-replay-harness.7.md +4 -0
  150. package/docs/multi-agent-operator-ux.7.md +4 -0
  151. package/docs/node-snapshot-diff-replay.7.md +4 -0
  152. package/docs/observability-cost-accounting.7.md +4 -0
  153. package/docs/project-index.md +143 -71
  154. package/docs/real-execution-backends.7.md +4 -0
  155. package/docs/release-and-migration.7.md +4 -0
  156. package/docs/release-tooling.7.md +4 -0
  157. package/docs/run-registry-control-plane.7.md +4 -0
  158. package/docs/run-retention-reclamation.7.md +25 -0
  159. package/docs/state-explosion-management.7.md +4 -0
  160. package/docs/team-collaboration.7.md +4 -0
  161. package/docs/web-desktop-workbench.7.md +4 -0
  162. package/manifest/plugin.manifest.json +1 -1
  163. package/manifest/source-context-profiles.json +9 -13
  164. package/package.json +1 -1
  165. package/scripts/agents/codex-agent.js +34 -4
  166. package/scripts/agents/cw-attest-wrap.js +2 -2
  167. package/scripts/bump-version.js +4 -3
  168. package/scripts/canonical-apps.js +4 -4
  169. package/scripts/children/batch-delegate-child.js +30 -10
  170. package/scripts/dogfood-architecture-review.js +2 -3
  171. package/scripts/dogfood-release.js +3 -3
  172. package/scripts/gen-parity-doc.js +15 -2
  173. package/scripts/golden-path.js +4 -4
  174. package/scripts/onramp-check.js +1 -1
  175. package/scripts/parity-check.js +38 -21
  176. package/scripts/release-flow.js +9 -3
  177. package/scripts/sync-project-index.js +51 -27
  178. package/scripts/validate-run-state-schema.js +2 -2
  179. package/scripts/version-sync-check.js +30 -30
  180. package/dist/candidate-scoring.js +0 -729
  181. package/dist/capability-core.js +0 -1186
  182. package/dist/capability-registry.js +0 -879
  183. package/dist/cli/command-surface.js +0 -490
  184. package/dist/cli/format.js +0 -56
  185. package/dist/cli/handlers/audit.js +0 -82
  186. package/dist/cli/handlers/blackboard.js +0 -81
  187. package/dist/cli/handlers/candidate.js +0 -40
  188. package/dist/cli/handlers/clones.js +0 -34
  189. package/dist/cli/handlers/collaboration.js +0 -61
  190. package/dist/cli/handlers/eval.js +0 -40
  191. package/dist/cli/handlers/maintenance.js +0 -107
  192. package/dist/cli/handlers/multi-agent.js +0 -165
  193. package/dist/cli/handlers/node.js +0 -41
  194. package/dist/cli/handlers/operational.js +0 -155
  195. package/dist/cli/handlers/operator.js +0 -146
  196. package/dist/cli/handlers/registry.js +0 -68
  197. package/dist/cli/handlers/run.js +0 -153
  198. package/dist/cli/handlers/scheduling.js +0 -132
  199. package/dist/cli/handlers/workbench.js +0 -41
  200. package/dist/cli/handlers/worker.js +0 -45
  201. package/dist/cli/run-summary.js +0 -45
  202. package/dist/clones.js +0 -162
  203. package/dist/collaboration.js +0 -726
  204. package/dist/commit.js +0 -592
  205. package/dist/compare.js +0 -18
  206. package/dist/coordinator/classify.js +0 -45
  207. package/dist/coordinator/paths.js +0 -42
  208. package/dist/coordinator/util.js +0 -126
  209. package/dist/coordinator.js +0 -990
  210. package/dist/daemon.js +0 -44
  211. package/dist/dispatch.js +0 -250
  212. package/dist/drive.js +0 -827
  213. package/dist/error-feedback.js +0 -419
  214. package/dist/evidence-grounding.js +0 -184
  215. package/dist/execution-backend/agent.js +0 -294
  216. package/dist/execution-backend/probes.js +0 -112
  217. package/dist/execution-backend/util.js +0 -47
  218. package/dist/execution-backend.js +0 -961
  219. package/dist/gates.js +0 -48
  220. package/dist/harness.js +0 -61
  221. package/dist/loop-expansion.js +0 -60
  222. package/dist/mcp/tool-call.js +0 -434
  223. package/dist/mcp/tool-definitions.js +0 -1040
  224. package/dist/mcp-surface.js +0 -30
  225. package/dist/multi-agent/graph.js +0 -84
  226. package/dist/multi-agent/helpers.js +0 -141
  227. package/dist/multi-agent/ids.js +0 -20
  228. package/dist/multi-agent/paths.js +0 -22
  229. package/dist/multi-agent-eval/normalize.js +0 -51
  230. package/dist/multi-agent-eval.js +0 -678
  231. package/dist/multi-agent-host.js +0 -777
  232. package/dist/multi-agent.js +0 -984
  233. package/dist/node-projection.js +0 -59
  234. package/dist/node-snapshot.js +0 -260
  235. package/dist/operator-ux.js +0 -631
  236. package/dist/orchestrator/app-operations.js +0 -211
  237. package/dist/orchestrator/audit-operations.js +0 -182
  238. package/dist/orchestrator/candidate-operations.js +0 -117
  239. package/dist/orchestrator/cli-options.js +0 -294
  240. package/dist/orchestrator/collaboration-operations.js +0 -86
  241. package/dist/orchestrator/feedback-operations.js +0 -81
  242. package/dist/orchestrator/host-operations.js +0 -78
  243. package/dist/orchestrator/lifecycle-operations.js +0 -626
  244. package/dist/orchestrator/migration-operations.js +0 -44
  245. package/dist/orchestrator/multi-agent-operations.js +0 -362
  246. package/dist/orchestrator/topology-operations.js +0 -84
  247. package/dist/orchestrator.js +0 -917
  248. package/dist/pipeline-runner.js +0 -285
  249. package/dist/reclamation/hash.js +0 -72
  250. package/dist/reclamation.js +0 -812
  251. package/dist/reporter.js +0 -67
  252. package/dist/run-export.js +0 -784
  253. package/dist/run-registry/derive.js +0 -175
  254. package/dist/run-registry/format.js +0 -124
  255. package/dist/run-registry/gc.js +0 -251
  256. package/dist/run-registry/policy.js +0 -16
  257. package/dist/run-registry/queue.js +0 -115
  258. package/dist/run-registry.js +0 -850
  259. package/dist/run-state-schema.js +0 -68
  260. package/dist/scheduling.js +0 -184
  261. package/dist/state-explosion.js +0 -1014
  262. package/dist/state.js +0 -367
  263. package/dist/telemetry-ledger.js +0 -196
  264. package/dist/topology.js +0 -565
  265. package/dist/triggers.js +0 -184
  266. package/dist/trust-audit.js +0 -644
  267. package/dist/types/blackboard.js +0 -2
  268. package/dist/types/candidate.js +0 -2
  269. package/dist/types/collaboration.js +0 -2
  270. package/dist/types/core.js +0 -2
  271. package/dist/types/drive.js +0 -10
  272. package/dist/types/error-feedback.js +0 -2
  273. package/dist/types/evidence-reasoning.js +0 -2
  274. package/dist/types/execution-backend.js +0 -2
  275. package/dist/types/multi-agent.js +0 -2
  276. package/dist/types/observability.js +0 -2
  277. package/dist/types/pipeline.js +0 -2
  278. package/dist/types/reclamation.js +0 -8
  279. package/dist/types/report-bundle.js +0 -6
  280. package/dist/types/result.js +0 -2
  281. package/dist/types/run-registry.js +0 -2
  282. package/dist/types/run.js +0 -2
  283. package/dist/types/sandbox.js +0 -2
  284. package/dist/types/schedule.js +0 -2
  285. package/dist/types/state-node.js +0 -2
  286. package/dist/types/topology.js +0 -2
  287. package/dist/types/trust.js +0 -2
  288. package/dist/types/workbench.js +0 -2
  289. package/dist/types/worker.js +0 -2
  290. package/dist/types/workflow-app.js +0 -2
  291. package/dist/types.js +0 -44
  292. package/dist/util/fingerprint.js +0 -19
  293. package/dist/util/fingerprint.test.js +0 -27
  294. package/dist/verifier.js +0 -78
  295. package/dist/version.js +0 -8
  296. package/dist/workbench-host.js +0 -182
  297. package/dist/workbench.js +0 -192
  298. package/dist/worker-accept/acceptance.js +0 -114
  299. package/dist/worker-accept/blackboard-fanout.js +0 -80
  300. package/dist/worker-accept/blackboard-linkage.js +0 -19
  301. package/dist/worker-accept/context.js +0 -2
  302. package/dist/worker-accept/telemetry-ledger.js +0 -126
  303. package/dist/worker-accept/validation.js +0 -77
  304. package/dist/worker-accept/verifier-completion.js +0 -73
  305. package/dist/worker-isolation/helpers.js +0 -51
  306. package/dist/worker-isolation/paths.js +0 -46
  307. package/dist/worker-isolation.js +0 -656
  308. package/dist/workflow-api.js +0 -131
  309. /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
  }