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
@@ -0,0 +1,667 @@
1
+ "use strict";
2
+ // shell/pipeline-cli.ts — CLI-facing entry points for the pipeline/drive
3
+ // spine: planRun, runDrivePreview, runDriveStep, quickstartRun,
4
+ // dispatchRun, recordResultRun, commitRun.
5
+ //
6
+ // MILESTONE 6+7 (combined). Wires the pieces built in shell/pipeline.ts,
7
+ // shell/drive.ts, shell/dispatch.ts, shell/commit.ts, shell/worker-
8
+ // isolation.ts, shell/workflow-app-loader.ts into the shapes
9
+ // core/capability-table.ts's CLI bindings call.
10
+ //
11
+ // Evidence: SPEC/pipeline-run.md "CLI / MCP surface (capability layer)".
12
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ var desc = Object.getOwnPropertyDescriptor(m, k);
15
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
16
+ desc = { enumerable: true, get: function() { return m[k]; } };
17
+ }
18
+ Object.defineProperty(o, k2, desc);
19
+ }) : (function(o, m, k, k2) {
20
+ if (k2 === undefined) k2 = k;
21
+ o[k2] = m[k];
22
+ }));
23
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
24
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
25
+ }) : function(o, v) {
26
+ o["default"] = v;
27
+ });
28
+ var __importStar = (this && this.__importStar) || (function () {
29
+ var ownKeys = function(o) {
30
+ ownKeys = Object.getOwnPropertyNames || function (o) {
31
+ var ar = [];
32
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
33
+ return ar;
34
+ };
35
+ return ownKeys(o);
36
+ };
37
+ return function (mod) {
38
+ if (mod && mod.__esModule) return mod;
39
+ var result = {};
40
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
41
+ __setModuleDefault(result, mod);
42
+ return result;
43
+ };
44
+ })();
45
+ Object.defineProperty(exports, "__esModule", { value: true });
46
+ exports.planRun = planRun;
47
+ exports.runDrivePreview = runDrivePreview;
48
+ exports.runDriveStep = runDriveStep;
49
+ exports.quickstartRun = quickstartRun;
50
+ exports.dispatchRun = dispatchRun;
51
+ exports.recordResultRun = recordResultRun;
52
+ exports.commitRun = commitRun;
53
+ const fs = __importStar(require("node:fs"));
54
+ const path = __importStar(require("node:path"));
55
+ const pipeline_1 = require("./pipeline");
56
+ const workflow_app_loader_1 = require("./workflow-app-loader");
57
+ const drive_1 = require("./drive");
58
+ const dispatch_1 = require("./dispatch");
59
+ const commit_1 = require("./commit");
60
+ const worker_isolation_1 = require("./worker-isolation");
61
+ const observability_1 = require("./observability");
62
+ const run_store_1 = require("./run-store");
63
+ const report_1 = require("./report");
64
+ const agent_config_1 = require("./agent-config");
65
+ const remote_source_1 = require("./remote-source");
66
+ const trust_audit_1 = require("./trust-audit");
67
+ const report_cli_1 = require("./report-cli");
68
+ const QUICKSTART_DEFAULT_APP = "architecture-review";
69
+ /** True when `value` is a truthy CLI flag (present, or "true"/"1"/"yes"/"on"). */
70
+ function truthyFlag(value) {
71
+ if (value === true)
72
+ return true;
73
+ if (typeof value === "string")
74
+ return /^(1|true|yes|on)$/i.test(value.trim());
75
+ return false;
76
+ }
77
+ /** First non-empty string among the given arg values (mirrors the old
78
+ * build's optionalString read over several alias keys). */
79
+ function firstString(...values) {
80
+ for (const value of values) {
81
+ if (typeof value === "string" && value.trim())
82
+ return value.trim();
83
+ }
84
+ return undefined;
85
+ }
86
+ /** Shell-quote a token for a copy-pasteable next command (byte-exact to the
87
+ * old build's shellWord). */
88
+ function shellWord(value) {
89
+ if (/^[A-Za-z0-9_./:@%+=,-]+$/.test(value))
90
+ return value;
91
+ return `'${value.replace(/'/g, "'\\''")}'`;
92
+ }
93
+ /** The copy-pasteable `cw quickstart …` line the `--check` payload echoes,
94
+ * weaving in --bundle / --with-trust-key / --strict-signatures so the
95
+ * suggested resume preserves bundle intent. Byte-exact port of the old
96
+ * build's quickstartNextCommand. */
97
+ function quickstartNextCommand(appId, repo, args) {
98
+ const parts = ["cw", "quickstart", shellWord(appId), "--repo", shellWord(repo)];
99
+ const question = firstString(args.question);
100
+ if (question)
101
+ parts.push("--question", shellWord(question));
102
+ const command = firstString(args.agentCommand, args["agent-command"]);
103
+ if (command)
104
+ parts.push("--agent-command", shellWord(command));
105
+ if (truthyFlag(args.bundle))
106
+ parts.push("--bundle");
107
+ const trustKey = firstString(args["with-trust-key"], args.withTrustKey, args.trustKey);
108
+ if (trustKey)
109
+ parts.push("--with-trust-key", shellWord(trustKey));
110
+ if (truthyFlag(args["strict-signatures"]) || truthyFlag(args.strictSignatures) || truthyFlag(args.strictSigs))
111
+ parts.push("--strict-signatures");
112
+ return parts.join(" ");
113
+ }
114
+ /** Runtime keys that must NEVER leak into run.inputs (they are drive/CLI
115
+ * plumbing, not workflow-declared inputs). Byte-exact to the old
116
+ * build's DRIVE_RUNTIME_KEYS list. */
117
+ const RUNTIME_KEYS = new Set([
118
+ "once", "now", "preview", "step", "drive", "json", "format", "run", "runId", "cwd",
119
+ "agentCommand", "agent-command", "agentArgs", "agent-args", "agentEndpoint", "agent-endpoint",
120
+ "agentModel", "agent-model", "agentTimeoutMs", "agent-timeout-ms", "resume", "incremental",
121
+ "concurrency", "link", "ref", "branch", "refresh", "check", "app", "appId", "workflowId", "question", "repo",
122
+ ]);
123
+ /** Byte-exact port of the old build's `normalizeInputs`
124
+ * (src/orchestrator/lifecycle-operations.ts:465-480): repeated `--arg
125
+ * key=value` pairs unpack into inputs (key = text before the first "=",
126
+ * value = the rest re-joined with "="); `repo` copies to `cwd` when `cwd`
127
+ * is not already set. Per SPEC/orchestrator.md's "Plan input rules" and
128
+ * SPEC/workflow-apps.md. */
129
+ function planInputsFor(args) {
130
+ const out = {};
131
+ for (const [key, value] of Object.entries(args)) {
132
+ if (key === "arg") {
133
+ const pairs = Array.isArray(value) ? value : [value];
134
+ for (const pair of pairs) {
135
+ const [argKey, ...rest] = String(pair).split("=");
136
+ out[argKey] = rest.join("=");
137
+ }
138
+ continue;
139
+ }
140
+ if (RUNTIME_KEYS.has(key))
141
+ continue;
142
+ out[key] = value;
143
+ }
144
+ if (typeof args.repo === "string")
145
+ out.repo = args.repo;
146
+ if (typeof args.question === "string")
147
+ out.question = args.question;
148
+ // An explicit --cwd is stripped by RUNTIME_KEYS above, but the old build
149
+ // honored it for the run anchor. Re-add it (like repo) so a caller-supplied
150
+ // cwd is not silently dropped to process.cwd() — a cross-request bleed.
151
+ if (typeof args.cwd === "string" && args.cwd.trim())
152
+ out.cwd = args.cwd;
153
+ if (out.repo && !out.cwd)
154
+ out.cwd = out.repo;
155
+ return out;
156
+ }
157
+ function invocationCwd(args) {
158
+ return typeof args.cwd === "string" && args.cwd.trim() ? path.resolve(args.cwd) : process.cwd();
159
+ }
160
+ /** True for a value that counts as "not supplied" — byte-exact to the old
161
+ * build's cli-options.isMissing (undefined / null / empty string). */
162
+ function isMissingInput(value) {
163
+ return value === undefined || value === null || value === "";
164
+ }
165
+ /** Resolve a workflow app for `plan`/`run --drive` over the SAME surface `cw
166
+ * list` shows — bundled apps AND legacy `<name>.workflow.js` files. The old
167
+ * build's plan() resolved via loadWorkflowAppById (full discovery over both the
168
+ * workflows/ and apps/ roots — app-operations.ts:64), so any id `cw list`
169
+ * surfaces is plannable. v2's fast-path loadWorkflowApp only reads
170
+ * apps/<id>/app.json, so a legacy workflow-file wrapper (e.g.
171
+ * legacy-research-synthesis) that `list` CAN resolve died with "Workflow app
172
+ * not found". Fall back to the full-discovery record (loadWorkflowAppRecordById)
173
+ * and adapt it to the minimal LoadedWorkflowApp plan() consumes. */
174
+ function resolveWorkflowAppForPlan(appId) {
175
+ try {
176
+ return (0, workflow_app_loader_1.loadWorkflowApp)(appId);
177
+ }
178
+ catch (error) {
179
+ if (!(error instanceof workflow_app_loader_1.WorkflowAppNotFoundError))
180
+ throw error;
181
+ const record = (0, workflow_app_loader_1.loadWorkflowAppRecordById)(appId);
182
+ const author = typeof record.app.author === "string" ? record.app.author : record.app.author?.name;
183
+ return {
184
+ id: record.app.id,
185
+ title: record.app.title,
186
+ summary: record.app.summary || record.app.workflow.summary || "",
187
+ version: record.app.version,
188
+ ...(author !== undefined ? { author } : {}),
189
+ workflow: record.app.workflow,
190
+ sandboxProfiles: record.app.sandboxProfiles || record.app.workflow.sandboxProfiles || [],
191
+ sourcePath: record.source.manifestPath || record.source.path,
192
+ };
193
+ }
194
+ }
195
+ /** `cw plan <workflowId>` — real: loads the app, plans a fresh run,
196
+ * returns the canonical plan summary. */
197
+ function planRun(args) {
198
+ const appId = String(args.workflowId || args.app || QUICKSTART_DEFAULT_APP);
199
+ // POLA: `cw plan <app>` does NOT default repo to the caller's cwd (unlike the
200
+ // one-command quickstart). The old build validated required inputs FIRST
201
+ // (lifecycle-operations.ts validateInputs, message `Missing required input
202
+ // --<name>`), so a missing --repo surfaces the copy-pasteable `-dir` recovery
203
+ // line (cli/entry.ts recoveryHint's "missing"+"repo" branch) instead of a
204
+ // silent cwd-anchored run. Auto-filling repo here would hide that recovery.
205
+ const app = resolveWorkflowAppForPlan(appId);
206
+ const planInputs = planInputsFor(args);
207
+ for (const declared of app.workflow.inputs || []) {
208
+ if (declared.required && isMissingInput(planInputs[declared.name])) {
209
+ throw new Error(`Missing required input --${declared.name}`);
210
+ }
211
+ }
212
+ const run = (0, pipeline_1.plan)(app, planInputs);
213
+ // `pendingTasks` is the canonical plan-payload key both `cw plan` and
214
+ // `cw_plan` carry (old build src/capability-core.ts:79 planSummary:
215
+ // `pendingTasks: run.tasks.filter(status === "pending").length`, and
216
+ // SPEC/workflow-apps.md). `taskCount` stays as a harmless extra.
217
+ const pendingTasks = run.tasks.filter((task) => task.status === "pending").length;
218
+ return { schemaVersion: 1, runId: run.id, workflowId: run.workflow.id, statePath: run.paths.state, reportPath: run.paths.report, pendingTasks, taskCount: run.tasks.length };
219
+ }
220
+ function runDrivePreview(args) {
221
+ const runId = String(args.runId || args.run || "");
222
+ const cwd = invocationCwd(args);
223
+ return (0, drive_1.drivePreview)(runId, cwd, args);
224
+ }
225
+ /** `cw run <app|--run id> --drive [--once]` — plans a fresh run (unless
226
+ * `--run` continues an existing one) and drives it. */
227
+ function runDriveStep(args) {
228
+ const existingRunId = String(args.runId || args.run || "");
229
+ const options = {
230
+ once: Boolean(args.once),
231
+ now: typeof args.now === "string" ? args.now : undefined,
232
+ args,
233
+ concurrency: args.concurrency !== undefined ? Number(args.concurrency) : undefined,
234
+ incremental: Boolean(args.incremental),
235
+ };
236
+ if (existingRunId) {
237
+ const cwd = invocationCwd(args);
238
+ const run = (0, run_store_1.loadRunFromCwd)(existingRunId, cwd);
239
+ return (0, drive_1.drive)(existingRunId, run.cwd, options);
240
+ }
241
+ const appId = String(args.appId || args.app || args.positionalApp || "");
242
+ if (!appId)
243
+ throw new Error("run --drive requires an app id (or --run <run-id> to continue)");
244
+ if (!args.repo && !args.cwd)
245
+ args.repo = invocationCwd(args);
246
+ const app = (0, workflow_app_loader_1.loadWorkflowApp)(appId);
247
+ const run = (0, pipeline_1.plan)(app, planInputsFor(args));
248
+ return (0, drive_1.drive)(run.id, run.cwd, options);
249
+ }
250
+ /** `cw quickstart [app] --check` — read-only preflight: does the app
251
+ * resolve, is the repo readable/writable, is a question set, is an
252
+ * agent backend configured. Never plans or writes a run. Byte-exact
253
+ * port of the old build's `quickstartCheck` (src/capability-core.ts),
254
+ * local-repo path only (the --link/remote preflight variant is not
255
+ * ported — no conformance case exercises it). */
256
+ function quickstartCheck(appId, args, remoteCandidate) {
257
+ // `--link`/URL preflight: validate the URL SHAPE + tooling WITHOUT fetching
258
+ // (a clone is heavy + side-effecting; --check stays read-only). Swaps the
259
+ // local-repo readability checks for link + tooling. `repo` carries the
260
+ // sanitized URL so the result reports what would be reviewed.
261
+ if (remoteCandidate)
262
+ return remoteQuickstartCheck(appId, args, remoteCandidate);
263
+ const base = invocationCwd(args);
264
+ const repoArg = typeof args.repo === "string" && args.repo.trim() ? args.repo : base;
265
+ const repo = path.resolve(base, repoArg);
266
+ const checks = [];
267
+ try {
268
+ (0, workflow_app_loader_1.showWorkflowApp)(appId);
269
+ checks.push({ name: "app", status: "ok", detail: `Workflow app ${appId} is available.` });
270
+ }
271
+ catch {
272
+ checks.push({
273
+ name: "app",
274
+ status: "blocked",
275
+ detail: `Workflow app ${appId} is not available.`,
276
+ fix: "Run `cw app list` and choose one of the listed app ids.",
277
+ });
278
+ }
279
+ let repoReadable = false;
280
+ let repoStateWritable = false;
281
+ try {
282
+ const stat = fs.statSync(repo);
283
+ repoReadable = stat.isDirectory();
284
+ if (!repoReadable)
285
+ throw new Error("not a directory");
286
+ fs.accessSync(repo, fs.constants.R_OK);
287
+ checks.push({ name: "repo", status: "ok", detail: `Repository path is readable (${repo}).` });
288
+ }
289
+ catch {
290
+ checks.push({
291
+ name: "repo",
292
+ status: "blocked",
293
+ detail: `Repository path is not readable (${repo}).`,
294
+ fix: "Pass --repo PATH for a readable repository directory.",
295
+ });
296
+ }
297
+ try {
298
+ const cwDir = path.join(repo, ".cw");
299
+ fs.accessSync(fs.existsSync(cwDir) ? cwDir : repo, fs.constants.W_OK);
300
+ repoStateWritable = repoReadable;
301
+ checks.push({ name: "repo-state", status: "ok", detail: "Run state location is writable." });
302
+ }
303
+ catch {
304
+ checks.push({
305
+ name: "repo-state",
306
+ status: "blocked",
307
+ detail: "Run state location is not writable.",
308
+ fix: "Use a writable repo, fix directory permissions, or pass --repo to a writable checkout.",
309
+ });
310
+ }
311
+ if (typeof args.question === "string" && args.question.trim()) {
312
+ checks.push({ name: "question", status: "ok", detail: "Question is set." });
313
+ }
314
+ else {
315
+ checks.push({ name: "question", status: "blocked", detail: "Question is missing.", fix: "Pass --question TEXT." });
316
+ }
317
+ if ((0, agent_config_1.agentConfigured)(args)) {
318
+ checks.push({ name: "agent", status: "ok", detail: "Agent backend is configured." });
319
+ }
320
+ else {
321
+ checks.push({
322
+ name: "agent",
323
+ status: "blocked",
324
+ detail: "No agent backend is configured.",
325
+ fix: 'Pass --agent-command "claude -p", set $CW_AGENT_COMMAND, or use --agent-command builtin:claude.',
326
+ });
327
+ }
328
+ // --bundle preflight: a completed run sealed into a bundle re-verifies offline
329
+ // only with a public trust key. Warn (not block) by default; block only under
330
+ // --strict-signatures where an unkeyed bundle would fail verification.
331
+ if (truthyFlag(args.bundle)) {
332
+ const trustKey = firstString(args["with-trust-key"], args.withTrustKey, args.trustKey, args.pubkey) || process.env.CW_AGENT_ATTEST_PUBKEY;
333
+ if (trustKey) {
334
+ checks.push({ name: "bundle-trust-key", status: "ok", detail: "Bundle trust public key is configured." });
335
+ }
336
+ else if (truthyFlag(args["strict-signatures"]) || truthyFlag(args.strictSignatures) || truthyFlag(args.strictSigs)) {
337
+ checks.push({ name: "bundle-trust-key", status: "blocked", detail: "Strict signature verification needs a public trust key.", fix: "Pass --with-trust-key PATH or set $CW_AGENT_ATTEST_PUBKEY." });
338
+ }
339
+ else {
340
+ checks.push({ name: "bundle-trust-key", status: "warn", detail: "No public trust key is configured; unsigned or unkeyed bundles may verify with reduced signature proof.", fix: "Pass --with-trust-key PATH to embed the public key." });
341
+ }
342
+ }
343
+ const ok = checks.every((check) => check.status !== "blocked") && repoStateWritable;
344
+ return { schemaVersion: 1, mode: "check", ok, appId, repo, checks, nextCommand: quickstartNextCommand(appId, repo, args) };
345
+ }
346
+ /** `--check` for a `--link`/URL review: validates the URL shape + git tooling
347
+ * WITHOUT fetching (byte-behavior port of the old build's remoteQuickstartCheck).
348
+ * `repo` carries the sanitized URL. */
349
+ function remoteQuickstartCheck(appId, args, candidate) {
350
+ const validation = (0, remote_source_1.validateRemoteUrl)(candidate);
351
+ const checks = [];
352
+ try {
353
+ (0, workflow_app_loader_1.showWorkflowApp)(appId);
354
+ checks.push({ name: "app", status: "ok", detail: `Workflow app ${appId} is available.` });
355
+ }
356
+ catch {
357
+ checks.push({ name: "app", status: "blocked", detail: `Workflow app ${appId} is not available.`, fix: "Run `cw app list` and choose one of the listed app ids." });
358
+ }
359
+ if (validation.ok) {
360
+ checks.push({ name: "link", status: "ok", detail: `Remote source is a valid ${validation.kind} URL (${validation.url}).` });
361
+ }
362
+ else {
363
+ checks.push({ name: "link", status: "blocked", detail: `Remote source is not usable: ${validation.reason}.`, fix: "Pass a git URL (https/ssh/git/file or git@host:repo)." });
364
+ }
365
+ if ((0, remote_source_1.gitAvailable)()) {
366
+ checks.push({ name: "tooling", status: "ok", detail: "git is available to clone the remote." });
367
+ }
368
+ else {
369
+ checks.push({ name: "tooling", status: "blocked", detail: "git was not found on PATH.", fix: "Install git, then re-run." });
370
+ }
371
+ if (typeof args.question === "string" && args.question.trim()) {
372
+ checks.push({ name: "question", status: "ok", detail: "Question is set." });
373
+ }
374
+ else {
375
+ checks.push({ name: "question", status: "blocked", detail: "Question is missing.", fix: "Pass --question TEXT." });
376
+ }
377
+ if ((0, agent_config_1.agentConfigured)(args)) {
378
+ checks.push({ name: "agent", status: "ok", detail: "Agent backend is configured." });
379
+ }
380
+ else {
381
+ checks.push({ name: "agent", status: "blocked", detail: "No agent backend is configured.", fix: 'Pass --agent-command "claude -p", set $CW_AGENT_COMMAND, or use --agent-command builtin:claude.' });
382
+ }
383
+ const ok = checks.every((check) => check.status !== "blocked");
384
+ const question = firstString(args.question);
385
+ const nextCommand = `cw quickstart ${shellWord(appId)} --link ${shellWord(validation.url)}${question ? ` --question ${shellWord(question)}` : ""}`;
386
+ return { schemaVersion: 1, mode: "check", ok, appId, repo: validation.url, checks, nextCommand };
387
+ }
388
+ /** `cw quickstart [app] --question ...` — composes plan -> runDrive ->
389
+ * report in one call. Default app is architecture-review. `--check` is a
390
+ * read-only preflight that never plans/drives/writes (see
391
+ * `quickstartCheck` above). `--preview` is a read-only next-step
392
+ * projection (never drives), `--resume` advances one step (no --run) or
393
+ * continues a named run to completion (--run <id>) — both ported byte-for-
394
+ * byte from the old build's src/capability-core.ts quickstart(). */
395
+ function quickstartRun(args) {
396
+ const appId = String(args.appId || args.app || args.workflowId || QUICKSTART_DEFAULT_APP);
397
+ // Remote source: a `--link <url>` — or a URL passed to `--repo`/`-dir` — is
398
+ // materialized to a LOCAL checkout HERE (capability/shell layer). Cloning is
399
+ // non-deterministic network I/O and must never enter the replay-deterministic
400
+ // core, so we rewrite `args.repo`/`args.cwd` to the local path; everything
401
+ // downstream is a normal local run.
402
+ const linkArg = typeof args.link === "string" && args.link.trim() ? args.link.trim() : undefined;
403
+ const repoArgRaw = typeof args.repo === "string" && args.repo.trim() ? args.repo.trim() : undefined;
404
+ const remoteCandidate = linkArg || (repoArgRaw && (0, remote_source_1.isRemoteUrl)(repoArgRaw) ? repoArgRaw : undefined);
405
+ if (!remoteCandidate && !args.repo && !args.cwd)
406
+ args.repo = invocationCwd(args);
407
+ if (Boolean(args.check))
408
+ return quickstartCheck(appId, args, remoteCandidate);
409
+ // Materialize the remote NOW — after `--check` (never fetches) and before any
410
+ // plan/drive — so the core only ever sees the local checkout. Fails closed: a
411
+ // bad URL / blocked scheme / missing git / fetch failure throws here.
412
+ let remoteSource;
413
+ if (remoteCandidate) {
414
+ remoteSource = (0, remote_source_1.materializeRemote)(remoteCandidate, {
415
+ ref: typeof args.ref === "string" ? args.ref : typeof args.branch === "string" ? args.branch : undefined,
416
+ refresh: Boolean(args.refresh),
417
+ });
418
+ args.repo = remoteSource.localPath;
419
+ args.cwd = remoteSource.localPath;
420
+ // Record the origin as plan INPUTS so it rides into run.inputs → the report
421
+ // header (report.ts renders `- Source: url@sha` from run.inputs.sourceUrl).
422
+ args.sourceUrl = remoteSource.url;
423
+ args.sourceCommit = remoteSource.commit;
424
+ if (remoteSource.ref)
425
+ args.sourceRef = remoteSource.ref;
426
+ }
427
+ // `--resume`: a discoverability flag over the existing continuation. With no
428
+ // `--run`, advance exactly ONE step (reuse the `--once` path) and print a
429
+ // copy-pasteable continue line; with `--run <id>`, continue that run to
430
+ // completion (the default drive). It adds no new execution path. Byte-exact to
431
+ // the old build's src/capability-core.ts quickstart().
432
+ const resume = Boolean(args.resume);
433
+ const existingRunId = String(args.runId || args.run || "");
434
+ const resumeRunId = resume && existingRunId ? existingRunId : undefined;
435
+ // `--preview`: read-only, deterministic next-step projection (no spawn, no
436
+ // commit). Plan a fresh run (the read-only first verb) then project the next
437
+ // drive step. Never drives.
438
+ if (Boolean(args.preview)) {
439
+ let previewRunId = existingRunId;
440
+ let repoCwd = typeof args.cwd === "string" && args.cwd.trim() ? args.cwd : typeof args.repo === "string" ? args.repo : undefined;
441
+ if (!previewRunId) {
442
+ const run = resolveWorkflowAppForPlan(appId);
443
+ const planned = (0, pipeline_1.plan)(run, planInputsFor(args));
444
+ previewRunId = planned.id;
445
+ repoCwd = planned.cwd;
446
+ }
447
+ const target = repoCwd && fs.existsSync(repoCwd) ? repoCwd : invocationCwd(args);
448
+ return (0, drive_1.drivePreview)(previewRunId, target, args);
449
+ }
450
+ const options = {
451
+ // `--resume` with no run id advances a SINGLE step (reuse `--once`), so a
452
+ // newcomer WITNESSES the stop-then-resume; `--resume --run <id>` continues to
453
+ // completion (the default drive). Non-resume paths keep the caller's --once.
454
+ once: Boolean(args.once) || (resume && !resumeRunId),
455
+ now: typeof args.now === "string" ? args.now : undefined,
456
+ args,
457
+ concurrency: args.concurrency !== undefined ? Number(args.concurrency) : undefined,
458
+ incremental: Boolean(args.incremental),
459
+ };
460
+ let run;
461
+ if (existingRunId) {
462
+ run = (0, run_store_1.loadRunFromCwd)(existingRunId, invocationCwd(args));
463
+ }
464
+ else {
465
+ run = (0, pipeline_1.plan)(resolveWorkflowAppForPlan(appId), planInputsFor(args));
466
+ }
467
+ const result = (0, drive_1.drive)(run.id, run.cwd, options);
468
+ const finalRun = (0, run_store_1.loadRunFromCwd)(run.id, run.cwd);
469
+ (0, report_1.writeReport)(finalRun);
470
+ // Tamper-evident provenance: bind the remote origin (url@sha) into the run's
471
+ // hash-chained trust-audit log so `cw audit verify` re-proves where the code
472
+ // came from. Best-effort — the origin is already in run.inputs/report/result.
473
+ if (remoteSource) {
474
+ try {
475
+ (0, trust_audit_1.recordTrustAuditEvent)(finalRun, {
476
+ kind: remoteSource.kind === "archive" ? "source.download" : "source.clone",
477
+ decision: "recorded",
478
+ source: "operator-recorded",
479
+ metadata: { url: remoteSource.url, commit: remoteSource.commit, ref: remoteSource.ref || null, kind: remoteSource.kind, depth: 1 },
480
+ });
481
+ }
482
+ catch {
483
+ /* provenance is additive; never fail a completed review over an audit hiccup */
484
+ }
485
+ }
486
+ // --bundle: after a COMPLETE drive, seal the run into a portable, self-verified
487
+ // bundle so the one command yields a client-verifiable artifact. Pure composition
488
+ // of reportBundleCli (export sealed + offline self-verify); spawns nothing. Gated
489
+ // on completion: a partial/blocked run is NEVER sealed. Run-state resolution
490
+ // anchors to the run's OWN repo (run.cwd) — quickstart runs cross-directory — but
491
+ // OUTPUT paths resolve against the CALLER's cwd so artifacts land where the
492
+ // operator ran the command. Byte-behavior port of the old build's quickstart().
493
+ const wantsBundle = truthyFlag(args.bundle);
494
+ let bundle;
495
+ if (wantsBundle && result.status === "complete") {
496
+ const callerBase = invocationCwd(args);
497
+ const outArg = firstString(args.output, args.path, args.archive);
498
+ const extractArg = firstString(args["extract-report"], args.extractReport, args.extractReportTo);
499
+ bundle = (0, report_cli_1.reportBundleCli)(result.runId, {
500
+ ...args,
501
+ cwd: run.cwd,
502
+ output: path.resolve(callerBase, outArg || `${result.runId}.cwrun.json`),
503
+ ...(extractArg ? { "extract-report": path.resolve(callerBase, extractArg) } : {}),
504
+ });
505
+ }
506
+ // Human-facing triage `hint` (stderr-side; absent on a clean completion so the
507
+ // default payload is byte-identical). Byte-exact wording to the old build's
508
+ // src/capability-core.ts quickstart(): the fail-closed "not configured …
509
+ // DELEGATES" line reaffirms the red line; the resume/once lines are copy-paste
510
+ // continue commands.
511
+ let hint;
512
+ if (!result.agentConfigured) {
513
+ hint =
514
+ "agent backend not configured — set CW_AGENT_COMMAND (e.g. \"claude -p\") or pass --agent-command, then re-run. The one command DELEGATES worker execution to YOUR agent; it never executes a model itself.";
515
+ }
516
+ else if (result.status === "parked") {
517
+ hint = `a worker parked past its retry budget — inspect: cw run show ${result.runId}`;
518
+ }
519
+ else if (result.status === "blocked") {
520
+ hint = `the drive is blocked — inspect: cw run drive ${result.runId}`;
521
+ }
522
+ else if (result.status === "in-progress") {
523
+ hint = resume
524
+ ? `one step advanced — continue: cw quickstart ${appId} --run ${result.runId} --resume${wantsBundle ? " --bundle" : ""}`
525
+ : `one step advanced (--once) — continue: cw quickstart ${appId} --run ${result.runId} --once`;
526
+ }
527
+ // --bundle on a run that did not complete is a NO-OP, not silence: tell the
528
+ // operator why nothing was sealed (the Rule of Silence permits a human hint).
529
+ if (wantsBundle && result.status !== "complete") {
530
+ hint = `${hint ? `${hint} ` : ""}--bundle skipped: the run did not complete (status=${result.status}); no bundle was sealed.`;
531
+ }
532
+ // Byte-exact to the old build's quickstart() return shape
533
+ // (src/capability-core.ts): `appId` is the resolved app id (the
534
+ // argument, or its architecture-review default), distinct from
535
+ // `workflowId` which is the driven run's own workflow id (equal for a
536
+ // top-level run, different for a sub-workflow hop). `remote` is present only
537
+ // for a --link/URL source, so a local-repo run stays byte-identical.
538
+ // `resumedFrom` is stamped ONLY when an explicit --run was continued
539
+ // (conditional spread keeps the key absent on the fresh/default path).
540
+ return {
541
+ appId,
542
+ ...result,
543
+ hint,
544
+ ...(resumeRunId ? { resumedFrom: resumeRunId } : {}),
545
+ // `bundle` is present only when --bundle sealed a completed run (conditional
546
+ // spread keeps the key absent on the default path → byte-identical output).
547
+ ...(bundle ? { bundle } : {}),
548
+ ...(remoteSource
549
+ ? { remote: { url: remoteSource.url, commit: remoteSource.commit, kind: remoteSource.kind, cached: remoteSource.cached, ...(remoteSource.ref ? { ref: remoteSource.ref } : {}) } }
550
+ : {}),
551
+ };
552
+ }
553
+ function dispatchRun(args) {
554
+ const runId = String(args.runId);
555
+ const run = (0, run_store_1.loadRunFromCwd)(runId, invocationCwd(args));
556
+ // parseArgv keys long flags in kebab-case; accept camelCase as a fallback.
557
+ const flag = (kebab, camel) => {
558
+ const v = args[kebab] ?? args[camel];
559
+ return typeof v === "string" && v.trim() ? v : undefined;
560
+ };
561
+ const manifest = (0, dispatch_1.createDispatchManifest)(run, args.limit !== undefined ? Number(args.limit) : undefined, {
562
+ sandboxProfileId: typeof args.sandbox === "string" ? args.sandbox : undefined,
563
+ sandbox: typeof args.sandbox === "string" ? args.sandbox : undefined,
564
+ backendId: typeof args.backend === "string" ? args.backend : undefined,
565
+ multiAgentRunId: flag("multi-agent-run", "multiAgentRun"),
566
+ multiAgentGroupId: flag("multi-agent-group", "multiAgentGroup"),
567
+ multiAgentRoleId: flag("multi-agent-role", "multiAgentRole"),
568
+ multiAgentFanoutId: flag("multi-agent-fanout", "multiAgentFanout"),
569
+ });
570
+ if (manifest.dispatchId) {
571
+ (0, commit_1.commitState)(run, `dispatch:${manifest.dispatchId}`);
572
+ (0, run_store_1.saveCheckpoint)(run);
573
+ (0, report_1.writeReport)(run);
574
+ }
575
+ return manifest;
576
+ }
577
+ function recordResultRun(args) {
578
+ const runId = String(args.runId);
579
+ const taskId = String(args.taskId);
580
+ const resultPath = String(args.resultPath);
581
+ const run = (0, run_store_1.loadRunFromCwd)(runId, invocationCwd(args));
582
+ const task = run.tasks.find((t) => t.id === taskId);
583
+ if (!task || !task.workerId)
584
+ throw new Error(`Unknown task id for run ${runId}: ${taskId}`);
585
+ const absolute = path.resolve(resultPath);
586
+ // A result path inside a system directory is never accepted (POLA): the
587
+ // operator file gets copied into the worker's result.md, so a /etc/passwd
588
+ // source would smuggle system content into a run. Byte-behavior port of the
589
+ // old build's recordResult system-directory blacklist.
590
+ if (/^\/(etc|bin|sbin|usr|Library|System|Applications|boot|dev|proc|sys|root|var\/log|var\/run)\//.test(absolute)) {
591
+ throw new Error(`Result path must not be a system directory: ${resultPath}`);
592
+ }
593
+ if (!fs.existsSync(absolute))
594
+ throw new Error(`Result file does not exist: ${resultPath}`);
595
+ const workerId = String(task.workerId);
596
+ // Host-attested `cw result <run> <task> <file>` intake: the operator hands CW
597
+ // an EXTERNAL result file that lives OUTSIDE the worker's read-only write
598
+ // boundary. The old task-level recordResult (lifecycle-operations.ts:279-280)
599
+ // COPIED that external file into the run's results area and recorded the
600
+ // internal path — it never ran the external path through validateSandboxWrite.
601
+ // v2 collapsed the two intakes into recordWorkerOutput, which sandbox-validates
602
+ // its input against the worker boundary, so a bare external path is rejected
603
+ // ("write path is outside sandbox profile <id>"). Restore the copy-in: stage
604
+ // the operator file at the worker's OWN result.md (which IS inside the write
605
+ // boundary), then record that internal path exactly like a driven worker.
606
+ const manifest = (0, worker_isolation_1.showWorkerManifest)(run, workerId);
607
+ fs.mkdirSync(path.dirname(manifest.resultPath), { recursive: true });
608
+ fs.copyFileSync(absolute, manifest.resultPath);
609
+ const output = (0, worker_isolation_1.recordWorkerOutput)(run, workerId, manifest.resultPath);
610
+ // Host-attested token usage (v0.1.31): record it verbatim as provenance when
611
+ // the operator supplied `--usage-*` flags; CW never synthesizes usage. The old
612
+ // task-level recordResult set `task.usage = usage` (lifecycle-operations.ts:286)
613
+ // and its unit was the TASK. v2 records through recordWorkerOutput, which gives
614
+ // the worker an `output` record — so the observability usage UNIT becomes the
615
+ // WORKER (deriveUsageTotals reads worker.usage for workers with output, and
616
+ // EXCLUDES that task). Attach the usage to the worker scope so the report counts
617
+ // it as an attested unit; also stamp task.usage for byte-parity with the old
618
+ // task-level record.
619
+ const usage = (0, observability_1.parseUsageFromArgs)(args, new Date().toISOString());
620
+ if (usage) {
621
+ task.usage = usage;
622
+ const scope = (0, worker_isolation_1.getWorkerScope)(run, workerId);
623
+ if (scope)
624
+ scope.usage = usage;
625
+ }
626
+ // Byte-exact to the old build's orchestrator recordWorkerOutput()
627
+ // wrapper: an accepted result is its own checkpoint commit, not just a
628
+ // bare saveCheckpoint (SPEC/pipeline-run.md's persist-ordering rule).
629
+ (0, commit_1.commitState)(run, `worker:${workerId}:result`);
630
+ (0, run_store_1.saveCheckpoint)(run);
631
+ (0, report_1.writeReport)(run);
632
+ return output;
633
+ }
634
+ /** `cw commit <run-id>` — byte-exact port of the old build's
635
+ * `orchestrator/lifecycle-operations.ts`'s `commit()`: the CLI/MCP
636
+ * payload wraps the commit record as `{runId, commit}` (NOT the commit
637
+ * record at top level). Both the success AND the throw path write the
638
+ * report + checkpoint before returning/re-throwing — a gate failure
639
+ * still leaves the run's report/state current on disk. */
640
+ function commitRun(args) {
641
+ const runId = String(args.runId);
642
+ const run = (0, run_store_1.loadRunFromCwd)(runId, invocationCwd(args));
643
+ const allowCheckpoint = Boolean(args.allowUnverifiedCheckpoint || args["allow-unverified-checkpoint"]);
644
+ const hasGateOption = Boolean(args.verifier || args.verifierNode || args["verifier-node"] || args.candidate || args.selection);
645
+ try {
646
+ const commit = (0, commit_1.commitState)(run, {
647
+ reason: typeof args.reason === "string" && args.reason ? args.reason : "manual",
648
+ verifierNodeId: (typeof args.verifier === "string" && args.verifier) ||
649
+ (typeof args.verifierNode === "string" && args.verifierNode) ||
650
+ (typeof args["verifier-node"] === "string" && args["verifier-node"]) ||
651
+ undefined,
652
+ candidateId: typeof args.candidate === "string" ? args.candidate : undefined,
653
+ selectionId: typeof args.selection === "string" ? args.selection : undefined,
654
+ verifierGated: hasGateOption || !allowCheckpoint,
655
+ allowUnverifiedCheckpoint: allowCheckpoint,
656
+ source: "cli",
657
+ });
658
+ (0, report_1.writeReport)(run);
659
+ (0, run_store_1.saveCheckpoint)(run);
660
+ return { runId: run.id, commit };
661
+ }
662
+ catch (error) {
663
+ (0, report_1.writeReport)(run);
664
+ (0, run_store_1.saveCheckpoint)(run);
665
+ throw error;
666
+ }
667
+ }