cool-workflow 0.1.98 → 0.2.1

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 (315) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/README.md +18 -7
  4. package/apps/architecture-review/app.json +2 -2
  5. package/apps/architecture-review/workflow.js +12 -12
  6. package/apps/architecture-review-fast/app.json +1 -1
  7. package/apps/end-to-end-golden-path/app.json +1 -1
  8. package/apps/pr-review-fix-ci/app.json +1 -1
  9. package/apps/release-cut/app.json +1 -1
  10. package/apps/research-synthesis/app.json +1 -1
  11. package/dist/cli/dispatch.js +236 -0
  12. package/dist/cli/entry.js +120 -0
  13. package/dist/cli/io.js +21 -4
  14. package/dist/cli/parseargv.js +157 -0
  15. package/dist/cli.js +6 -33
  16. package/dist/core/capability-table.js +3518 -0
  17. package/dist/core/format/help.js +314 -0
  18. package/dist/{state-explosion/format.js → core/format/state-explosion-text.js} +10 -1
  19. package/dist/core/hash.js +137 -0
  20. package/dist/core/multi-agent/candidate-scoring.js +219 -0
  21. package/dist/core/multi-agent/collaboration.js +481 -0
  22. package/dist/core/multi-agent/coordinator.js +515 -0
  23. package/dist/core/multi-agent/eval-replay.js +306 -0
  24. package/dist/core/multi-agent/runtime.js +929 -0
  25. package/dist/core/multi-agent/topology.js +298 -0
  26. package/dist/core/multi-agent/trust-policy.js +197 -0
  27. package/dist/core/pipeline/commit-gate.js +320 -0
  28. package/dist/{pipeline-contract.js → core/pipeline/contract.js} +24 -37
  29. package/dist/core/pipeline/dispatch.js +103 -0
  30. package/dist/core/pipeline/drive-decide.js +227 -0
  31. package/dist/core/pipeline/error-feedback.js +161 -0
  32. package/dist/core/pipeline/loop-expansion.js +124 -0
  33. package/dist/{result-normalize.js → core/pipeline/result-normalize.js} +14 -37
  34. package/dist/core/pipeline/runner.js +230 -0
  35. package/dist/{contract-migration.js → core/state/contract-migration.js} +68 -92
  36. package/dist/{state-migrations.js → core/state/migrations.js} +103 -96
  37. package/dist/core/state/node-projection.js +95 -0
  38. package/dist/core/state/node-snapshot.js +230 -0
  39. package/dist/core/state/run-paths.js +93 -0
  40. package/dist/{schema-validate.js → core/state/schema-validate.js} +35 -28
  41. package/dist/core/state/schema.js +50 -0
  42. package/dist/core/state/state-explosion/digest.js +243 -0
  43. package/dist/core/state/state-explosion/graph.js +527 -0
  44. package/dist/{state-explosion → core/state/state-explosion}/helpers.js +34 -3
  45. package/dist/core/state/state-explosion/report.js +187 -0
  46. package/dist/{state-explosion → core/state/state-explosion}/size.js +25 -7
  47. package/dist/{state-node.js → core/state/state-node.js} +90 -113
  48. package/dist/core/state/types.js +19 -0
  49. package/dist/{validation.js → core/state/validation.js} +64 -131
  50. package/dist/core/trust/evidence-grounding.js +134 -0
  51. package/dist/core/trust/ledger.js +199 -0
  52. package/dist/{telemetry-attestation.js → core/trust/telemetry-attestation.js} +94 -68
  53. package/dist/core/trust/telemetry-ledger.js +127 -0
  54. package/dist/core/types.js +20 -0
  55. package/dist/core/version.js +16 -0
  56. package/dist/{workflow-app-framework.js → core/workflow-apps/app-schema.js} +337 -426
  57. package/dist/mcp/dispatch.js +104 -0
  58. package/dist/mcp/server.js +144 -0
  59. package/dist/mcp-server.js +6 -84
  60. package/dist/{agent-config.js → shell/agent-config.js} +118 -71
  61. package/dist/shell/app-run-cli.js +88 -0
  62. package/dist/shell/audit-cli.js +259 -0
  63. package/dist/shell/audit-provenance.js +83 -0
  64. package/dist/shell/candidate-scoring-io.js +459 -0
  65. package/dist/shell/collaboration-io.js +264 -0
  66. package/dist/shell/commit-summary.js +104 -0
  67. package/dist/shell/commit.js +290 -0
  68. package/dist/shell/coordinator-io.js +476 -0
  69. package/dist/shell/demo-cli.js +19 -0
  70. package/dist/shell/dispatch.js +162 -0
  71. package/dist/shell/doctor.js +292 -0
  72. package/dist/shell/drive.js +885 -0
  73. package/dist/shell/error-feedback-io.js +305 -0
  74. package/dist/shell/eval-io.js +473 -0
  75. package/dist/{multi-agent-eval/format.js → shell/eval-text.js} +78 -49
  76. package/dist/{evidence-reasoning.js → shell/evidence-reasoning.js} +124 -255
  77. package/dist/shell/exec-backend-cli.js +88 -0
  78. package/dist/shell/execution-backend/agent.js +507 -0
  79. package/dist/shell/execution-backend/ci.js +15 -0
  80. package/dist/shell/execution-backend/container.js +69 -0
  81. package/dist/shell/execution-backend/envelopes.js +55 -0
  82. package/dist/shell/execution-backend/local.js +113 -0
  83. package/dist/shell/execution-backend/probes.js +175 -0
  84. package/dist/shell/execution-backend/registry.js +402 -0
  85. package/dist/shell/execution-backend/remote.js +128 -0
  86. package/dist/shell/execution-backend/types.js +11 -0
  87. package/dist/shell/feedback-cli.js +81 -0
  88. package/dist/shell/feedback-operations.js +48 -0
  89. package/dist/shell/fs-atomic.js +276 -0
  90. package/dist/shell/harness.js +98 -0
  91. package/dist/shell/ledger-cli.js +212 -0
  92. package/dist/shell/ledger-io.js +169 -0
  93. package/dist/shell/man-cli.js +89 -0
  94. package/dist/shell/metrics-cli.js +100 -0
  95. package/dist/shell/multi-agent-cli.js +1002 -0
  96. package/dist/shell/multi-agent-host.js +563 -0
  97. package/dist/shell/multi-agent-io.js +387 -0
  98. package/dist/{multi-agent-operator-ux.js → shell/multi-agent-operator-ux.js} +234 -191
  99. package/dist/shell/node-store.js +124 -0
  100. package/dist/{observability/format.js → shell/observability-format.js} +7 -1
  101. package/dist/{observability/intake.js → shell/observability-intake.js} +79 -56
  102. package/dist/{observability.js → shell/observability.js} +159 -332
  103. package/dist/{onramp.js → shell/onramp.js} +11 -0
  104. package/dist/{operator-ux/format.js → shell/operator-ux-text.js} +250 -239
  105. package/dist/shell/operator-ux.js +431 -0
  106. package/dist/shell/orchestrator.js +231 -0
  107. package/dist/shell/pipeline-cli.js +667 -0
  108. package/dist/shell/pipeline.js +217 -0
  109. package/dist/shell/reclamation-io.js +1366 -0
  110. package/dist/shell/registry-cli.js +344 -0
  111. package/dist/{remote-source.js → shell/remote-source.js} +2 -2
  112. package/dist/shell/report-cli.js +101 -0
  113. package/dist/shell/report-view-cli.js +117 -0
  114. package/dist/{orchestrator → shell}/report.js +289 -282
  115. package/dist/shell/reporter.js +62 -0
  116. package/dist/shell/run-export-cli.js +106 -0
  117. package/dist/shell/run-export.js +680 -0
  118. package/dist/shell/run-registry-io.js +1014 -0
  119. package/dist/shell/run-store.js +164 -0
  120. package/dist/{sandbox-profile.js → shell/sandbox-profile.js} +134 -95
  121. package/dist/{scheduler.js → shell/scheduler-io.js} +257 -48
  122. package/dist/shell/scheduling-io.js +321 -0
  123. package/dist/shell/state-cli.js +181 -0
  124. package/dist/shell/state-explosion-cli.js +197 -0
  125. package/dist/shell/telemetry-cli.js +85 -0
  126. package/dist/{telemetry-demo.js → shell/telemetry-demo.js} +149 -119
  127. package/dist/shell/telemetry-ledger-io.js +132 -0
  128. package/dist/{term.js → shell/term.js} +36 -46
  129. package/dist/shell/topology-io.js +361 -0
  130. package/dist/shell/trust-audit.js +472 -0
  131. package/dist/{multi-agent-trust.js → shell/trust-policy-io.js} +67 -205
  132. package/dist/shell/verifier.js +48 -0
  133. package/dist/shell/workbench-host.js +258 -0
  134. package/dist/shell/workbench-text.js +18 -0
  135. package/dist/shell/workbench.js +170 -0
  136. package/dist/shell/worker-cli.js +124 -0
  137. package/dist/shell/worker-isolation.js +852 -0
  138. package/dist/shell/workflow-app-loader.js +650 -0
  139. package/docs/agent-delegation-drive.7.md +4 -0
  140. package/docs/cli-mcp-parity.7.md +282 -219
  141. package/docs/contract-migration-tooling.7.md +4 -0
  142. package/docs/control-plane-scheduling.7.md +4 -0
  143. package/docs/durable-state-and-locking.7.md +4 -0
  144. package/docs/evidence-adoption-reasoning-chain.7.md +4 -0
  145. package/docs/execution-backends.7.md +4 -0
  146. package/docs/multi-agent-cli-mcp-surface.7.md +4 -0
  147. package/docs/multi-agent-eval-replay-harness.7.md +4 -0
  148. package/docs/multi-agent-operator-ux.7.md +4 -0
  149. package/docs/node-snapshot-diff-replay.7.md +4 -0
  150. package/docs/observability-cost-accounting.7.md +6 -1
  151. package/docs/project-index.md +137 -71
  152. package/docs/real-execution-backends.7.md +4 -0
  153. package/docs/release-and-migration.7.md +4 -0
  154. package/docs/release-tooling.7.md +4 -0
  155. package/docs/remote-source-review.7.md +4 -4
  156. package/docs/report-verifiable-bundle.7.md +1 -1
  157. package/docs/run-registry-control-plane.7.md +4 -0
  158. package/docs/run-retention-reclamation.7.md +25 -0
  159. package/docs/security-trust-hardening.7.md +3 -1
  160. package/docs/state-explosion-management.7.md +4 -0
  161. package/docs/team-collaboration.7.md +4 -0
  162. package/docs/web-desktop-workbench.7.md +15 -1
  163. package/manifest/plugin.manifest.json +1 -1
  164. package/manifest/source-context-profiles.json +9 -13
  165. package/package.json +1 -1
  166. package/scripts/agents/agent-adapter-core.js +22 -2
  167. package/scripts/agents/claude-p-agent.js +8 -3
  168. package/scripts/agents/cw-attest-wrap.js +2 -2
  169. package/scripts/agents/gemini-agent.js +5 -1
  170. package/scripts/agents/opencode-agent.js +5 -1
  171. package/scripts/bump-version.js +4 -3
  172. package/scripts/canonical-apps.js +4 -4
  173. package/scripts/dogfood-architecture-review.js +2 -3
  174. package/scripts/dogfood-release.js +3 -3
  175. package/scripts/gen-parity-doc.js +15 -2
  176. package/scripts/golden-path.js +4 -4
  177. package/scripts/onramp-check.js +1 -1
  178. package/scripts/parity-check.js +38 -21
  179. package/scripts/release-flow.js +2 -2
  180. package/scripts/sync-project-index.js +51 -27
  181. package/scripts/validate-run-state-schema.js +2 -2
  182. package/scripts/version-sync-check.js +30 -30
  183. package/dist/candidate-scoring.js +0 -729
  184. package/dist/capability-core.js +0 -1189
  185. package/dist/capability-registry.js +0 -885
  186. package/dist/cli/command-surface.js +0 -494
  187. package/dist/cli/format.js +0 -56
  188. package/dist/cli/handlers/audit.js +0 -82
  189. package/dist/cli/handlers/blackboard.js +0 -81
  190. package/dist/cli/handlers/candidate.js +0 -40
  191. package/dist/cli/handlers/clones.js +0 -34
  192. package/dist/cli/handlers/collaboration.js +0 -61
  193. package/dist/cli/handlers/eval.js +0 -40
  194. package/dist/cli/handlers/ledger.js +0 -169
  195. package/dist/cli/handlers/maintenance.js +0 -107
  196. package/dist/cli/handlers/multi-agent.js +0 -165
  197. package/dist/cli/handlers/node.js +0 -41
  198. package/dist/cli/handlers/operational.js +0 -155
  199. package/dist/cli/handlers/operator.js +0 -146
  200. package/dist/cli/handlers/registry.js +0 -68
  201. package/dist/cli/handlers/run.js +0 -153
  202. package/dist/cli/handlers/scheduling.js +0 -132
  203. package/dist/cli/handlers/workbench.js +0 -41
  204. package/dist/cli/handlers/worker.js +0 -45
  205. package/dist/cli/run-summary.js +0 -45
  206. package/dist/clones.js +0 -162
  207. package/dist/collaboration.js +0 -726
  208. package/dist/commit.js +0 -592
  209. package/dist/compare.js +0 -18
  210. package/dist/coordinator/classify.js +0 -45
  211. package/dist/coordinator/paths.js +0 -42
  212. package/dist/coordinator/util.js +0 -126
  213. package/dist/coordinator.js +0 -990
  214. package/dist/daemon.js +0 -44
  215. package/dist/dispatch.js +0 -250
  216. package/dist/doctor.js +0 -212
  217. package/dist/drive.js +0 -864
  218. package/dist/error-feedback.js +0 -419
  219. package/dist/evidence-grounding.js +0 -184
  220. package/dist/execution-backend/agent.js +0 -354
  221. package/dist/execution-backend/probes.js +0 -112
  222. package/dist/execution-backend/util.js +0 -47
  223. package/dist/execution-backend.js +0 -961
  224. package/dist/gates.js +0 -48
  225. package/dist/harness.js +0 -61
  226. package/dist/ledger.js +0 -313
  227. package/dist/loop-expansion.js +0 -60
  228. package/dist/mcp/tool-call.js +0 -470
  229. package/dist/mcp/tool-definitions.js +0 -1066
  230. package/dist/mcp-surface.js +0 -30
  231. package/dist/multi-agent/graph.js +0 -84
  232. package/dist/multi-agent/helpers.js +0 -141
  233. package/dist/multi-agent/ids.js +0 -20
  234. package/dist/multi-agent/paths.js +0 -22
  235. package/dist/multi-agent-eval/normalize.js +0 -51
  236. package/dist/multi-agent-eval.js +0 -678
  237. package/dist/multi-agent-host.js +0 -777
  238. package/dist/multi-agent.js +0 -984
  239. package/dist/node-projection.js +0 -59
  240. package/dist/node-snapshot.js +0 -260
  241. package/dist/operator-ux.js +0 -631
  242. package/dist/orchestrator/app-operations.js +0 -211
  243. package/dist/orchestrator/audit-operations.js +0 -182
  244. package/dist/orchestrator/candidate-operations.js +0 -117
  245. package/dist/orchestrator/cli-options.js +0 -294
  246. package/dist/orchestrator/collaboration-operations.js +0 -86
  247. package/dist/orchestrator/feedback-operations.js +0 -81
  248. package/dist/orchestrator/host-operations.js +0 -78
  249. package/dist/orchestrator/lifecycle-operations.js +0 -650
  250. package/dist/orchestrator/migration-operations.js +0 -44
  251. package/dist/orchestrator/multi-agent-operations.js +0 -362
  252. package/dist/orchestrator/topology-operations.js +0 -84
  253. package/dist/orchestrator.js +0 -925
  254. package/dist/pipeline-runner.js +0 -285
  255. package/dist/reclamation/hash.js +0 -72
  256. package/dist/reclamation.js +0 -812
  257. package/dist/reporter.js +0 -67
  258. package/dist/run-export.js +0 -815
  259. package/dist/run-registry/derive.js +0 -175
  260. package/dist/run-registry/format.js +0 -124
  261. package/dist/run-registry/gc.js +0 -251
  262. package/dist/run-registry/policy.js +0 -16
  263. package/dist/run-registry/queue.js +0 -115
  264. package/dist/run-registry.js +0 -850
  265. package/dist/run-state-schema.js +0 -68
  266. package/dist/scheduling.js +0 -184
  267. package/dist/state-explosion.js +0 -1014
  268. package/dist/state.js +0 -367
  269. package/dist/telemetry-ledger.js +0 -196
  270. package/dist/topology.js +0 -565
  271. package/dist/triggers.js +0 -184
  272. package/dist/trust-audit.js +0 -644
  273. package/dist/types/blackboard.js +0 -2
  274. package/dist/types/candidate.js +0 -2
  275. package/dist/types/collaboration.js +0 -2
  276. package/dist/types/core.js +0 -2
  277. package/dist/types/drive.js +0 -10
  278. package/dist/types/error-feedback.js +0 -2
  279. package/dist/types/evidence-reasoning.js +0 -2
  280. package/dist/types/execution-backend.js +0 -2
  281. package/dist/types/multi-agent.js +0 -2
  282. package/dist/types/observability.js +0 -2
  283. package/dist/types/pipeline.js +0 -2
  284. package/dist/types/reclamation.js +0 -8
  285. package/dist/types/report-bundle.js +0 -6
  286. package/dist/types/result.js +0 -2
  287. package/dist/types/run-registry.js +0 -2
  288. package/dist/types/run.js +0 -2
  289. package/dist/types/sandbox.js +0 -2
  290. package/dist/types/schedule.js +0 -2
  291. package/dist/types/state-node.js +0 -2
  292. package/dist/types/topology.js +0 -2
  293. package/dist/types/trust.js +0 -2
  294. package/dist/types/workbench.js +0 -2
  295. package/dist/types/worker.js +0 -2
  296. package/dist/types/workflow-app.js +0 -2
  297. package/dist/types.js +0 -44
  298. package/dist/util/fingerprint.js +0 -19
  299. package/dist/util/fingerprint.test.js +0 -27
  300. package/dist/verifier.js +0 -78
  301. package/dist/version.js +0 -8
  302. package/dist/workbench-host.js +0 -192
  303. package/dist/workbench.js +0 -192
  304. package/dist/worker-accept/acceptance.js +0 -114
  305. package/dist/worker-accept/blackboard-fanout.js +0 -80
  306. package/dist/worker-accept/blackboard-linkage.js +0 -19
  307. package/dist/worker-accept/context.js +0 -2
  308. package/dist/worker-accept/telemetry-ledger.js +0 -126
  309. package/dist/worker-accept/validation.js +0 -77
  310. package/dist/worker-accept/verifier-completion.js +0 -73
  311. package/dist/worker-isolation/helpers.js +0 -51
  312. package/dist/worker-isolation/paths.js +0 -46
  313. package/dist/worker-isolation.js +0 -656
  314. package/dist/workflow-api.js +0 -131
  315. /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
+ }