cool-workflow 0.2.3 → 0.2.5

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 (143) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/.gemini-plugin/mcp.json +10 -0
  4. package/.gemini-plugin/plugin.json +40 -0
  5. package/.opencode-plugin/mcp.json +10 -0
  6. package/.opencode-plugin/plugin.json +40 -0
  7. package/README.md +202 -48
  8. package/apps/architecture-review/app.json +1 -1
  9. package/apps/architecture-review-fast/app.json +1 -1
  10. package/apps/end-to-end-golden-path/app.json +1 -1
  11. package/apps/pr-review-fix-ci/app.json +1 -1
  12. package/apps/release-cut/app.json +1 -1
  13. package/apps/research-synthesis/app.json +1 -1
  14. package/dist/cli/dispatch.js +33 -4
  15. package/dist/cli/entry.js +11 -19
  16. package/dist/cli/global-flags.js +113 -0
  17. package/dist/cli/io.js +6 -20
  18. package/dist/cli/parseargv.js +7 -1
  19. package/dist/core/capability-data.js +339 -10
  20. package/dist/core/format/help.js +80 -4
  21. package/dist/core/format/recovery-hint.js +32 -0
  22. package/dist/core/multi-agent/collaboration.js +35 -6
  23. package/dist/core/multi-agent/runtime.js +7 -0
  24. package/dist/core/multi-agent/trust-policy.js +7 -1
  25. package/dist/core/pipeline/contract.js +7 -0
  26. package/dist/core/pipeline/error-feedback.js +2 -2
  27. package/dist/core/trust/evidence-grounding.js +13 -1
  28. package/dist/core/util/cli-args.js +55 -0
  29. package/dist/core/version.js +1 -1
  30. package/dist/mcp/dispatch.js +22 -2
  31. package/dist/mcp/server.js +124 -13
  32. package/dist/mcp-server.js +20 -0
  33. package/dist/shell/commit.js +8 -2
  34. package/dist/shell/coordinator-io.js +73 -1
  35. package/dist/shell/drive.js +129 -64
  36. package/dist/shell/error-feedback-io.js +6 -0
  37. package/dist/shell/execution-backend/agent.js +205 -24
  38. package/dist/shell/execution-backend/container.js +44 -10
  39. package/dist/shell/execution-backend/local.js +47 -10
  40. package/dist/shell/fs-atomic.js +93 -12
  41. package/dist/shell/ledger-cli.js +9 -2
  42. package/dist/shell/multi-agent-cli.js +5 -1
  43. package/dist/shell/onramp.js +48 -5
  44. package/dist/shell/pipeline-cli.js +20 -2
  45. package/dist/shell/pipeline.js +2 -1
  46. package/dist/shell/reclamation-io.js +76 -7
  47. package/dist/shell/registry-cli.js +4 -0
  48. package/dist/shell/run-export.js +52 -4
  49. package/dist/shell/run-registry-io.js +1 -0
  50. package/dist/shell/run-store.js +156 -0
  51. package/dist/shell/scheduler-io.js +101 -10
  52. package/dist/shell/telemetry-ledger-io.js +36 -24
  53. package/dist/shell/trust-audit.js +104 -10
  54. package/dist/shell/workbench-host.js +121 -10
  55. package/dist/shell/workbench.js +79 -5
  56. package/dist/shell/worker-isolation.js +1 -1
  57. package/dist/shell/workflow-app-loader.js +67 -1
  58. package/dist/wiring/capability-table/basics.js +7 -2
  59. package/dist/wiring/capability-table/exec-backend.js +42 -24
  60. package/dist/wiring/capability-table/multi-agent.js +69 -69
  61. package/dist/wiring/capability-table/pipeline.js +60 -28
  62. package/dist/wiring/capability-table/registry-core.js +29 -6
  63. package/dist/wiring/capability-table/reporting.js +45 -39
  64. package/dist/wiring/capability-table/scheduling-registry.js +66 -60
  65. package/dist/wiring/capability-table/state.js +34 -34
  66. package/dist/wiring/capability-table/trust-ledger.js +66 -24
  67. package/dist/wiring/capability-table/workflow-apps.js +15 -15
  68. package/docs/agent-delegation-drive.7.md +17 -0
  69. package/docs/cli-mcp-parity.7.md +43 -13
  70. package/docs/contract-migration-tooling.7.md +4 -0
  71. package/docs/control-plane-scheduling.7.md +38 -0
  72. package/docs/cross-agent-ledger.7.md +20 -8
  73. package/docs/durable-state-and-locking.7.md +4 -0
  74. package/docs/evidence-adoption-reasoning-chain.7.md +4 -0
  75. package/docs/execution-backends.7.md +4 -0
  76. package/docs/fix.7.md +4 -4
  77. package/docs/getting-started.md +40 -32
  78. package/docs/index.md +17 -0
  79. package/docs/launch/demo.tape +4 -3
  80. package/docs/mcp-app-surface.7.md +6 -0
  81. package/docs/multi-agent-cli-mcp-surface.7.md +4 -0
  82. package/docs/multi-agent-eval-replay-harness.7.md +4 -0
  83. package/docs/multi-agent-operator-ux.7.md +4 -0
  84. package/docs/node-snapshot-diff-replay.7.md +4 -0
  85. package/docs/observability-cost-accounting.7.md +4 -0
  86. package/docs/project-index.md +43 -9
  87. package/docs/real-execution-backends.7.md +4 -0
  88. package/docs/release-and-migration.7.md +4 -0
  89. package/docs/release-tooling.7.md +22 -0
  90. package/docs/routine.7.md +22 -0
  91. package/docs/run-registry-control-plane.7.md +4 -0
  92. package/docs/run-retention-reclamation.7.md +28 -3
  93. package/docs/state-explosion-management.7.md +4 -0
  94. package/docs/team-collaboration.7.md +4 -0
  95. package/docs/trust-audit-anchor.7.md +4 -0
  96. package/docs/web-desktop-workbench.7.md +26 -2
  97. package/docs/workflow-app-framework.7.md +31 -0
  98. package/manifest/plugin.manifest.json +1 -1
  99. package/manifest/source-context-profiles.json +4 -3
  100. package/package.json +5 -2
  101. package/scripts/agents/agent-adapter-core.js +26 -0
  102. package/scripts/agents/claude-p-agent.js +4 -1
  103. package/scripts/agents/codex-agent.js +4 -0
  104. package/scripts/agents/cw-attest-wrap.js +1 -1
  105. package/scripts/agents/gemini-agent.js +4 -0
  106. package/scripts/agents/opencode-agent.js +5 -0
  107. package/scripts/block-unapproved-tag.js +160 -0
  108. package/scripts/bump-version.js +27 -10
  109. package/scripts/canonical-apps.js +4 -4
  110. package/scripts/children/batch-delegate-child.js +72 -4
  111. package/scripts/children/http-batch-delegate-child.js +132 -0
  112. package/scripts/children/http-delegate-child.js +8 -0
  113. package/scripts/dogfood-release.js +2 -2
  114. package/scripts/fake-date-for-reproduction.js +1 -1
  115. package/scripts/golden-path.js +4 -4
  116. package/scripts/lang-policy-check.js +82 -0
  117. package/scripts/purity-baseline.json +0 -30
  118. package/scripts/purity-gate.js +16 -3
  119. package/scripts/release-check.js +2 -1
  120. package/scripts/release-flow.js +264 -41
  121. package/scripts/release-gate.js +184 -0
  122. package/scripts/release-oneclick.js +438 -0
  123. package/scripts/release-tags.js +47 -0
  124. package/scripts/verdict-keygen.js +2 -2
  125. package/scripts/verify-bump-reproduction.js +193 -0
  126. package/scripts/verify-verdict-signature.js +1 -1
  127. package/scripts/version-sync-check.js +39 -22
  128. package/ui/workbench/app.css +41 -9
  129. package/ui/workbench/app.js +160 -27
  130. package/ui/workbench/index.html +2 -2
  131. package/docs/agent-framework.md +0 -177
  132. package/docs/designs/handoff-ledger.md +0 -145
  133. package/docs/dogfood/resume-drive-real-agent-2026-06-14.md +0 -40
  134. package/docs/launch/launch-kit.md +0 -195
  135. package/docs/launch/pre-launch-checklist.md +0 -53
  136. package/docs/readme-v0.1.87-full.md +0 -301
  137. package/docs/routines.md +0 -101
  138. package/docs/scheduled-tasks.md +0 -80
  139. package/scripts/agents/claude-p-agent.sh +0 -9
  140. package/scripts/block-unapproved-tag.sh +0 -60
  141. package/scripts/release-gate.sh +0 -94
  142. package/scripts/verify-bump-reproduction.sh +0 -148
  143. package/scripts/verify-container-selfref.js +0 -64
@@ -6,7 +6,7 @@
6
6
  // retyped).
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
8
  const registry_core_1 = require("./registry-core");
9
- const io_1 = require("../../cli/io");
9
+ const cli_args_1 = require("../../core/util/cli-args");
10
10
  // MILESTONE 8 (ledger, telemetry, trust-audit, tamper/bundle demos) CLI
11
11
  // bindings: ledger propose|review|verify|apply|list, telemetry verify,
12
12
  // audit verify, demo tamper|bundle, report bundle|verify-bundle. Handler
@@ -18,10 +18,13 @@ const io_1 = require("../../cli/io");
18
18
  // cli/parseargv.ts) even though dispatchTable now handles it as a real
19
19
  // row — a known, preserved wart.
20
20
  // ---------------------------------------------------------------------
21
- const ledger_cli_1 = require("../../shell/ledger-cli");
22
21
  // This file is required at startup for every command. Loading these shell
23
22
  // modules only when their handler runs, not at import time, keeps that
24
- // load cost out of commands that never touch telemetry/audit/demo/report.
23
+ // load cost out of commands that never touch ledger/telemetry/audit/demo/
24
+ // report.
25
+ function loadLedgerCli() {
26
+ return require("../../shell/ledger-cli");
27
+ }
25
28
  function loadTelemetryCli() {
26
29
  return require("../../shell/telemetry-cli");
27
30
  }
@@ -40,81 +43,120 @@ function loadReportCli() {
40
43
  (0, registry_core_1.attachCliBinding)("ledger.propose", {
41
44
  path: ["ledger", "propose"],
42
45
  jsonMode: "default",
43
- handler: (args) => ({ json: (0, ledger_cli_1.ledgerProposeCli)(args.options) }),
46
+ handler: (args) => ({ json: loadLedgerCli().ledgerProposeCli(args.options) }),
47
+ // UI/UX fix: `cw help ledger` used to list only the one-line summary for
48
+ // each row, so a first-run user had no way to learn these flags without
49
+ // reading source. Real flag names verified against ledgerProposeCli's
50
+ // required()/stringOption() calls above.
51
+ flags: [
52
+ { name: "--from AGENT/REPO", summary: "Who is making the change." },
53
+ { name: "--to AGENT/REPO", summary: "Who should get the change." },
54
+ { name: "--title TEXT", summary: "A short name for the change." },
55
+ { name: "--rationale TEXT", summary: "Why the change should happen." },
56
+ { name: "--files LIST", summary: "The files the change touches, as a comma list." },
57
+ { name: "--diff PATCH", summary: "The change as one diff file." },
58
+ ],
59
+ // ledgerProposeCli reads exactly from/to/title/rationale/files/diff
60
+ // (shell/ledger-cli.ts) — the list above is complete, so the dispatcher
61
+ // may warn about an unknown flag (TTY-only; see cli/global-flags.ts).
62
+ flagsComplete: true,
44
63
  });
45
- registry_core_1.REGISTRY_BY_CAPABILITY.get("ledger.propose").mcp.handler = (args) => (0, ledger_cli_1.ledgerProposeMcp)(args);
64
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("ledger.propose").mcp.handler = (args) => loadLedgerCli().ledgerProposeMcp(args);
46
65
  (0, registry_core_1.attachCliBinding)("ledger.review", {
47
66
  path: ["ledger", "review"],
48
67
  jsonMode: "default",
49
- handler: (args) => ({ json: (0, ledger_cli_1.ledgerReviewCli)(args.options) }),
68
+ handler: (args) => ({ json: loadLedgerCli().ledgerReviewCli(args.options) }),
69
+ // Real flag names verified against ledgerReviewCli's required()/
70
+ // stringOption() calls above — NOT the same flag set as ledger propose
71
+ // (this row needs --target/--verdict/--findings, not --title/
72
+ // --rationale/--diff).
73
+ flags: [
74
+ { name: "--from AGENT/REPO", summary: "Who is doing the review." },
75
+ { name: "--to AGENT/REPO", summary: "Who made the change being reviewed." },
76
+ { name: "--target ID", summary: "The id of the proposal or PR to review." },
77
+ { name: "--verdict approved|rejected", summary: "The result of the review." },
78
+ { name: "--findings TEXT", summary: "Notes on what the review found." },
79
+ ],
80
+ // ledgerReviewCli reads exactly from/to/target/verdict/findings
81
+ // (shell/ledger-cli.ts) — the list above is complete, so the dispatcher
82
+ // may warn about an unknown flag (TTY-only; see cli/global-flags.ts).
83
+ flagsComplete: true,
50
84
  });
51
- registry_core_1.REGISTRY_BY_CAPABILITY.get("ledger.review").mcp.handler = (args) => (0, ledger_cli_1.ledgerReviewMcp)(args);
85
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("ledger.review").mcp.handler = (args) => loadLedgerCli().ledgerReviewMcp(args);
52
86
  (0, registry_core_1.attachCliBinding)("ledger.verify", {
53
87
  path: ["ledger", "verify"],
54
88
  jsonMode: "default",
55
89
  handler: (args) => {
56
- const result = (0, ledger_cli_1.ledgerVerifyCli)(args.options);
90
+ const result = loadLedgerCli().ledgerVerifyCli(args.options);
57
91
  return { json: result, exitCode: result.ok ? undefined : 1 };
58
92
  },
59
93
  });
60
- registry_core_1.REGISTRY_BY_CAPABILITY.get("ledger.verify").mcp.handler = (args) => (0, ledger_cli_1.ledgerVerifyEntry)(args.entry);
94
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("ledger.verify").mcp.handler = (args) => loadLedgerCli().ledgerVerifyEntry(args.entry);
61
95
  (0, registry_core_1.attachCliBinding)("ledger.apply", {
62
96
  path: ["ledger", "apply"],
63
97
  jsonMode: "default",
64
98
  handler: (args) => {
65
- const result = (0, ledger_cli_1.ledgerApplyCli)(args.options);
99
+ const result = loadLedgerCli().ledgerApplyCli(args.options);
66
100
  return { json: result, exitCode: result.ok ? undefined : 1 };
67
101
  },
68
102
  });
69
- registry_core_1.REGISTRY_BY_CAPABILITY.get("ledger.apply").mcp.handler = (args) => (0, ledger_cli_1.ledgerApplyEntry)(args.entry);
103
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("ledger.apply").mcp.handler = (args) => loadLedgerCli().ledgerApplyEntry(args.entry);
70
104
  (0, registry_core_1.attachCliBinding)("ledger.list", {
71
105
  path: ["ledger", "list"],
72
106
  jsonMode: "default",
73
107
  handler: (args) => {
74
- const result = (0, ledger_cli_1.ledgerListCli)(args.options);
108
+ const result = loadLedgerCli().ledgerListCli(args.options);
75
109
  return { json: result, exitCode: result.allOk ? undefined : 1 };
76
110
  },
111
+ // UI/UX fix: `--dir` here named the ledger directory, while the global
112
+ // front door (cli/entry.ts) treats `--dir` as an alias of `--repo` for
113
+ // every command — one flag, two meanings. `--ledger-dir` is the
114
+ // unambiguous spelling; `--dir` stays as the legacy alias, byte-compat.
115
+ flags: [
116
+ { name: "--ledger-dir DIR", summary: "The ledger directory to read. Give it more than once to union-verify mirrors." },
117
+ { name: "--dir DIR", summary: "Old name for --ledger-dir (also the global --repo alias; kept working)." },
118
+ ],
77
119
  });
78
- registry_core_1.REGISTRY_BY_CAPABILITY.get("ledger.list").mcp.handler = (args) => (0, ledger_cli_1.ledgerListMcp)(args);
120
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("ledger.list").mcp.handler = (args) => loadLedgerCli().ledgerListMcp(args);
79
121
  (0, registry_core_1.attachCliBinding)("telemetry.verify", {
80
122
  path: ["telemetry", "verify"],
81
123
  jsonMode: "flag",
82
124
  handler: (args) => {
83
- const runId = (0, io_1.required)((0, io_1.optionalArg)(args.positionals[0]) || (0, io_1.optionalArg)(args.options.runId) || (0, io_1.optionalArg)(args.options.run), "run id");
125
+ const runId = (0, cli_args_1.required)((0, cli_args_1.optionalArg)(args.positionals[0]) || (0, cli_args_1.optionalArg)(args.options.runId) || (0, cli_args_1.optionalArg)(args.options.run), "run id");
84
126
  const result = loadTelemetryCli().telemetryVerifyCli(runId, args.options);
85
127
  return { json: result, text: loadTelemetryDemo().formatTelemetryVerify(result), exitCode: result.verified ? undefined : 1 };
86
128
  },
87
129
  });
88
- registry_core_1.REGISTRY_BY_CAPABILITY.get("telemetry.verify").mcp.handler = (args) => loadTelemetryCli().telemetryVerifyCli((0, io_1.required)((0, io_1.optionalArg)(args.runId), "run id"), args);
130
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("telemetry.verify").mcp.handler = (args) => loadTelemetryCli().telemetryVerifyCli((0, cli_args_1.required)((0, cli_args_1.optionalArg)(args.runId), "run id"), args);
89
131
  (0, registry_core_1.attachCliBinding)("audit.verify", {
90
132
  path: ["audit", "verify"],
91
133
  jsonMode: "default",
92
134
  handler: (args) => {
93
- const runId = (0, io_1.required)((0, io_1.optionalArg)(args.positionals[0]), "run id");
135
+ const runId = (0, cli_args_1.required)((0, cli_args_1.optionalArg)(args.positionals[0]), "run id");
94
136
  const result = loadAuditCli().auditVerifyCli(runId, args.options);
95
137
  return { json: result, exitCode: result.verified ? undefined : 1 };
96
138
  },
97
139
  });
98
- registry_core_1.REGISTRY_BY_CAPABILITY.get("audit.verify").mcp.handler = (args) => loadAuditCli().auditVerifyCli((0, io_1.required)((0, io_1.optionalArg)(args.runId), "run id"), args);
140
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("audit.verify").mcp.handler = (args) => loadAuditCli().auditVerifyCli((0, cli_args_1.required)((0, cli_args_1.optionalArg)(args.runId), "run id"), args);
99
141
  (0, registry_core_1.attachCliBinding)("audit.head", {
100
142
  path: ["audit", "head"],
101
143
  jsonMode: "default",
102
144
  handler: (args) => {
103
- const runId = (0, io_1.required)((0, io_1.optionalArg)(args.positionals[0]), "run id");
145
+ const runId = (0, cli_args_1.required)((0, cli_args_1.optionalArg)(args.positionals[0]), "run id");
104
146
  return { json: loadAuditCli().auditHeadCli(runId, args.options) };
105
147
  },
106
148
  });
107
- registry_core_1.REGISTRY_BY_CAPABILITY.get("audit.head").mcp.handler = (args) => loadAuditCli().auditHeadCli((0, io_1.required)((0, io_1.optionalArg)(args.runId), "run id"), args);
149
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("audit.head").mcp.handler = (args) => loadAuditCli().auditHeadCli((0, cli_args_1.required)((0, cli_args_1.optionalArg)(args.runId), "run id"), args);
108
150
  (0, registry_core_1.attachCliBinding)("audit.repair", {
109
151
  path: ["audit", "repair"],
110
152
  jsonMode: "default",
111
153
  handler: (args) => {
112
- const runId = (0, io_1.required)((0, io_1.optionalArg)(args.positionals[0]), "run id");
154
+ const runId = (0, cli_args_1.required)((0, cli_args_1.optionalArg)(args.positionals[0]), "run id");
113
155
  const result = loadAuditCli().auditRepairCli(runId, args.options);
114
156
  return { json: result, exitCode: result.outcome === "refused" ? 1 : undefined };
115
157
  },
116
158
  });
117
- registry_core_1.REGISTRY_BY_CAPABILITY.get("audit.repair").mcp.handler = (args) => loadAuditCli().auditRepairCli((0, io_1.required)((0, io_1.optionalArg)(args.runId), "run id"), args);
159
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("audit.repair").mcp.handler = (args) => loadAuditCli().auditRepairCli((0, cli_args_1.required)((0, cli_args_1.optionalArg)(args.runId), "run id"), args);
118
160
  (0, registry_core_1.addCliOnlyCapability)("demo.tamper", "Prove tamper-evidence: build a signed telemetry ledger, forge it, watch verification fail offline.", {
119
161
  path: ["demo", "tamper"],
120
162
  jsonMode: "flag",
@@ -135,17 +177,17 @@ registry_core_1.REGISTRY_BY_CAPABILITY.get("audit.repair").mcp.handler = (args)
135
177
  path: ["report", "bundle"],
136
178
  jsonMode: "default",
137
179
  handler: (args) => {
138
- const runId = (0, io_1.required)((0, io_1.optionalArg)(args.positionals[0]), "run id");
180
+ const runId = (0, cli_args_1.required)((0, cli_args_1.optionalArg)(args.positionals[0]), "run id");
139
181
  const result = loadReportCli().reportBundleCli(runId, args.options);
140
182
  return { json: result, exitCode: result.ok ? undefined : 1 };
141
183
  },
142
184
  });
143
- registry_core_1.REGISTRY_BY_CAPABILITY.get("report.bundle").mcp.handler = (args) => loadReportCli().reportBundleCli((0, io_1.required)((0, io_1.optionalArg)(args.runId), "run id"), args);
185
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("report.bundle").mcp.handler = (args) => loadReportCli().reportBundleCli((0, cli_args_1.required)((0, cli_args_1.optionalArg)(args.runId), "run id"), args);
144
186
  (0, registry_core_1.attachCliBinding)("report.verify-bundle", {
145
187
  path: ["report", "verify-bundle"],
146
188
  jsonMode: "flag",
147
189
  handler: (args) => {
148
- const archivePath = (0, io_1.required)((0, io_1.optionalArg)(args.positionals[0]), "bundle path");
190
+ const archivePath = (0, cli_args_1.required)((0, cli_args_1.optionalArg)(args.positionals[0]), "bundle path");
149
191
  const reportCli = loadReportCli();
150
192
  const result = reportCli.reportVerifyBundleCli({ ...args.options, archive: archivePath });
151
193
  return { json: result, text: reportCli.formatReportVerifyBundle(result), exitCode: result.ok ? undefined : 1 };
@@ -6,7 +6,7 @@
6
6
  // sed, not retyped).
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
8
  const registry_core_1 = require("./registry-core");
9
- const io_1 = require("../../cli/io");
9
+ const cli_args_1 = require("../../core/util/cli-args");
10
10
  // This whole module is required unconditionally at startup for EVERY
11
11
  // command (see wiring/capability-table/index.ts) — loading `next`'s and
12
12
  // `app.run`'s shell modules lazily, inside the handler that actually
@@ -44,24 +44,24 @@ registry_core_1.REGISTRY_BY_CAPABILITY.get("app.list").mcp.handler = () => loadW
44
44
  (0, registry_core_1.attachCliBinding)("app.show", {
45
45
  path: ["app", "show"],
46
46
  jsonMode: "default",
47
- handler: (args) => ({ json: loadWorkflowAppLoader().showWorkflowApp((0, io_1.required)(args.positionals[0], "workflow app id")) }),
47
+ handler: (args) => ({ json: loadWorkflowAppLoader().showWorkflowApp((0, cli_args_1.required)(args.positionals[0], "workflow app id")) }),
48
48
  });
49
- registry_core_1.REGISTRY_BY_CAPABILITY.get("app.show").mcp.handler = (args) => loadWorkflowAppLoader().showWorkflowApp((0, io_1.required)((0, io_1.optionalArg)(args.appId), "workflow app id"));
49
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("app.show").mcp.handler = (args) => loadWorkflowAppLoader().showWorkflowApp((0, cli_args_1.required)((0, cli_args_1.optionalArg)(args.appId), "workflow app id"));
50
50
  (0, registry_core_1.attachCliBinding)("app.validate", {
51
51
  path: ["app", "validate"],
52
52
  jsonMode: "default",
53
53
  handler: (args) => {
54
- const result = loadWorkflowAppLoader().validateWorkflowAppTarget((0, io_1.required)(args.positionals[0], "workflow app path or id"));
54
+ const result = loadWorkflowAppLoader().validateWorkflowAppTarget((0, cli_args_1.required)(args.positionals[0], "workflow app path or id"));
55
55
  return { json: result, exitCode: result.valid ? undefined : 1 };
56
56
  },
57
57
  });
58
- registry_core_1.REGISTRY_BY_CAPABILITY.get("app.validate").mcp.handler = (args) => loadWorkflowAppLoader().validateWorkflowAppTarget((0, io_1.required)((0, io_1.optionalArg)(args.target ?? args.appId), "workflow app path or id"));
58
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("app.validate").mcp.handler = (args) => loadWorkflowAppLoader().validateWorkflowAppTarget((0, cli_args_1.required)((0, cli_args_1.optionalArg)(args.target ?? args.appId), "workflow app path or id"));
59
59
  (0, registry_core_1.attachCliBinding)("app.init", {
60
60
  path: ["app", "init"],
61
61
  jsonMode: "default",
62
- handler: (args) => ({ json: loadWorkflowAppLoader().initWorkflowApp((0, io_1.required)(args.positionals[0], "app id"), args.options) }),
62
+ handler: (args) => ({ json: loadWorkflowAppLoader().initWorkflowApp((0, cli_args_1.required)(args.positionals[0], "app id"), args.options) }),
63
63
  });
64
- registry_core_1.REGISTRY_BY_CAPABILITY.get("app.init").mcp.handler = (args) => loadWorkflowAppLoader().initWorkflowApp((0, io_1.required)((0, io_1.optionalArg)(args.appId), "app id"), args);
64
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("app.init").mcp.handler = (args) => loadWorkflowAppLoader().initWorkflowApp((0, cli_args_1.required)((0, cli_args_1.optionalArg)(args.appId), "app id"), args);
65
65
  // `cw init <id>` — the standalone scaffold verb. v2 folds `init` into
66
66
  // `app.init` (the old build's legacy `.workflow.js` scaffold is gone), so
67
67
  // both surfaces route through initWorkflowApp, same as `cw app init`. The
@@ -71,15 +71,15 @@ registry_core_1.REGISTRY_BY_CAPABILITY.get("app.init").mcp.handler = (args) => l
71
71
  (0, registry_core_1.attachCliBinding)("init", {
72
72
  path: ["init"],
73
73
  jsonMode: "default",
74
- handler: (args) => ({ json: loadWorkflowAppLoader().initWorkflowApp((0, io_1.required)((0, io_1.optionalArg)(args.positionals[0]), "workflow id"), args.options) }),
74
+ handler: (args) => ({ json: loadWorkflowAppLoader().initWorkflowApp((0, cli_args_1.required)((0, cli_args_1.optionalArg)(args.positionals[0]), "workflow id"), args.options) }),
75
75
  });
76
- registry_core_1.REGISTRY_BY_CAPABILITY.get("init").mcp.handler = (args) => loadWorkflowAppLoader().initWorkflowApp((0, io_1.required)((0, io_1.optionalArg)(args.workflowId ?? args.appId), "workflow id"), args);
76
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("init").mcp.handler = (args) => loadWorkflowAppLoader().initWorkflowApp((0, cli_args_1.required)((0, cli_args_1.optionalArg)(args.workflowId ?? args.appId), "workflow id"), args);
77
77
  (0, registry_core_1.attachCliBinding)("app.package", {
78
78
  path: ["app", "package"],
79
79
  jsonMode: "default",
80
- handler: (args) => ({ json: loadWorkflowAppLoader().packageWorkflowApp((0, io_1.required)(args.positionals[0], "app id"), args.options) }),
80
+ handler: (args) => ({ json: loadWorkflowAppLoader().packageWorkflowApp((0, cli_args_1.required)(args.positionals[0], "app id"), args.options) }),
81
81
  });
82
- registry_core_1.REGISTRY_BY_CAPABILITY.get("app.package").mcp.handler = (args) => loadWorkflowAppLoader().packageWorkflowApp((0, io_1.required)((0, io_1.optionalArg)(args.appId), "app id"), args);
82
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("app.package").mcp.handler = (args) => loadWorkflowAppLoader().packageWorkflowApp((0, cli_args_1.required)((0, cli_args_1.optionalArg)(args.appId), "app id"), args);
83
83
  // `cw app run <app-id>` — plan+drive+report an app in one call. 2-token
84
84
  // cli.path found before the ["app"] usage catch-all; `appRunCli` reads the
85
85
  // app id from `appId`, so the first positional after "run" is forwarded as
@@ -87,7 +87,7 @@ registry_core_1.REGISTRY_BY_CAPABILITY.get("app.package").mcp.handler = (args) =
87
87
  (0, registry_core_1.attachCliBinding)("app.run", {
88
88
  path: ["app", "run"],
89
89
  jsonMode: "default",
90
- handler: (args) => ({ json: loadAppRunCli().appRunCli({ ...args.options, appId: (0, io_1.required)(args.positionals[0], "app id") }) }),
90
+ handler: (args) => ({ json: loadAppRunCli().appRunCli({ ...args.options, appId: (0, cli_args_1.required)(args.positionals[0], "app id") }) }),
91
91
  });
92
92
  // A 1-token `["app"]` row that exists ONLY to own the fixed usage string
93
93
  // for an unrecognized `app` subcommand (`app run` is not yet CLI-wired at
@@ -329,7 +329,7 @@ registry_core_1.REGISTRY_BY_CAPABILITY.get("app.package").mcp.handler = (args) =
329
329
  path: ["info"],
330
330
  jsonMode: "flag",
331
331
  handler: (args) => {
332
- const appId = (0, io_1.required)(args.positionals[0], "workflow app id");
332
+ const appId = (0, cli_args_1.required)(args.positionals[0], "workflow app id");
333
333
  const data = loadWorkflowAppLoader().showWorkflowApp(appId);
334
334
  return { json: data, text: `${(0, help_1.formatInfo)(appId, data)}\n` };
335
335
  },
@@ -351,9 +351,9 @@ registry_core_1.REGISTRY_BY_CAPABILITY.get("app.package").mcp.handler = (args) =
351
351
  (0, registry_core_1.attachCliBinding)("next", {
352
352
  path: ["next"],
353
353
  jsonMode: "default",
354
- handler: (args) => ({ json: loadStateCli().nextCli((0, io_1.required)((0, io_1.optionalArg)(args.positionals[0]), "run id"), args.options) }),
354
+ handler: (args) => ({ json: loadStateCli().nextCli((0, cli_args_1.required)((0, cli_args_1.optionalArg)(args.positionals[0]), "run id"), args.options) }),
355
355
  });
356
- registry_core_1.REGISTRY_BY_CAPABILITY.get("next").mcp.handler = (args) => loadStateCli().nextCli((0, io_1.required)((0, io_1.optionalArg)(args.runId), "run id"), args);
356
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("next").mcp.handler = (args) => loadStateCli().nextCli((0, cli_args_1.required)((0, cli_args_1.optionalArg)(args.runId), "run id"), args);
357
357
  // `ledger.propose`/`.review`/`.verify`/`.apply`/`.list` are documented
358
358
  // payload-probe opt-outs in the old build (each mints a fresh timestamped/
359
359
  // digested entry, or reads args that arrive by --file/stdin on the CLI vs
@@ -125,6 +125,19 @@ exactly one fixed step (injected `now`); bare `--drive` runs to the end or to a
125
125
  parked/blocked stop. `run drive <run-id>` (no `--step`) is the read-only, fixed
126
126
  preview of the next step.
127
127
 
128
+ ## Concurrent rounds (`--concurrency N`)
129
+
130
+ `--drive --concurrency N` (or an auto-width parallel phase) runs a whole round's
131
+ tasks in ONE window instead of one at a time. CW dispatches the round's tasks,
132
+ collects every agent outcome concurrently, then settles + accepts them in
133
+ DETERMINISTIC batch (task-id) order — so the wall-clock drops but the recorded
134
+ state, reports, and exit codes are byte-identical to a serial run. Both agent
135
+ shapes get real concurrency: a **CLI-binary** agent (`CW_AGENT_COMMAND`) runs
136
+ all children at once through the batch delegate child, and an **HTTP-endpoint**
137
+ agent (`CW_AGENT_ENDPOINT`) POSTs all N delegations at once through the HTTP
138
+ batch delegate child. A cache-hit task still settles on the serial path inside
139
+ the same round. An unconfigured agent still refuses (it is never a fake pass).
140
+
128
141
  ## Fail closed — probe vs refusal vs park
129
142
 
130
143
  - **Probe.** `backend probe agent` reports `readiness: "ready"` iff a
@@ -421,3 +434,7 @@ The one-command `cw -q` headline now routes the question and defaults the repo t
421
434
  0.2.2
422
435
 
423
436
  0.2.3
437
+
438
+ 0.2.4
439
+
440
+ 0.2.5
@@ -74,6 +74,29 @@ show/validate/apply/summary/graph, sandbox show/validate/choose/resolve, state
74
74
  summary refresh/show, `plan`, `approve`, `reject`, `comment.add`, `handoff`, and
75
75
  `review.policy`.
76
76
 
77
+ ## MCP Tool Hints and Version Negotiation
78
+
79
+ A `tools/list` entry may carry the standard MCP `annotations` field with
80
+ two behavior hints:
81
+
82
+ - `readOnlyHint: true` — the tool only reads; it makes no change on disk.
83
+ - `destructiveHint: true` (with `readOnlyHint: false`) — the tool deletes
84
+ stored data (the gc / delete sweeps: `cw_gc_run`, `cw_orphans_gc`,
85
+ `cw_clones_gc`, `cw_schedule_delete`, `cw_routine_delete`).
86
+
87
+ The hints come from one side table (`MCP_TOOL_ANNOTATIONS` in
88
+ `src/core/capability-data.ts`), and a tool goes in only after its handler
89
+ was read and checked by hand — a wrong "read-only" mark on a writing tool
90
+ would be a safety bug, so a tool we are not certain about carries no
91
+ `annotations` field at all. Absent beats wrong. The field is additive: a
92
+ client that does not know it simply does not read it.
93
+
94
+ The MCP `initialize` reply also negotiates the protocol version now: a
95
+ client that asks for a supported version gets that version echoed back,
96
+ and any other request falls back to the newest supported entry (see
97
+ `SUPPORTED_PROTOCOL_VERSIONS` in `src/mcp/server.ts`). With today's
98
+ one-entry list the reply bytes are unchanged.
99
+
77
100
  ## The Parity Matrix
78
101
 
79
102
  The matrix below is made from the live registry — one row per capability,
@@ -429,19 +452,22 @@ Parity is checked by `scripts/parity-check.js --check`, run by
429
452
  the registry, lists the live CLI commands and MCP tools, and fails closed on any
430
453
  of the rules above.
431
454
 
432
- The CLI dispatcher (`src/cli/command-surface.ts`) is being decomposed out of one
433
- large `switch` into per-command handler modules under `src/cli/handlers/`
434
- (`handle<Group>(args, runner)`), with `command-surface.ts` left as the thin
435
- router; shared helpers live in `src/cli/io.ts` (arg/JSON) and `src/cli/format.ts`
436
- (render). Carved so far: `workbench`, `clones`, `audit`, `worker`, `schedule`, `routine`,
437
- `sched`, `registry`, `queue`, `history`, `report`, `operator`, `graph`,
438
- `topology`, `summary`, `multi-agent`, `run`, `approve`, `reject`, `comment`,
439
- `handoff`, `review`, `blackboard`, `coordinator`, `eval`, `node`, `gc`,
440
- `telemetry`, `demo`, `feedback`, `metrics`, `migration`, `sandbox`, `backend`,
441
- `contract`, `candidate`. This is a pure code-move
442
- the command surface is unchanged and the parity scanner reads
443
- `dist/cli/handlers/*` so a subcommand `case` in a handler module still counts as a
444
- live CLI token.
455
+ The CLI dispatch surface is table-driven, not a hand-kept `switch`. The source
456
+ tree is small: `src/cli/entry.ts` (process entry), `src/cli/parseargv.ts`
457
+ (argv reading), `src/cli/dispatch.ts` (the one dispatcher), and `src/cli/io.ts`
458
+ (output helpers: `printJson`, `styledHelp`). `dispatch(args)` looks the parsed
459
+ command up in the capability table (`src/core/capability-table.ts`, put
460
+ together from the slices under `src/wiring/capability-table/`) and runs that
461
+ row's `cli.handler` a new capability is a new table row, never a new `case`.
462
+ The shared arg helpers (`required`, `optionalArg`, `wantsJson`) live in
463
+ `src/core/util/cli-args.ts`, a pure module used by the CLI and MCP sides of
464
+ every wiring slice; they were moved out of `cli/io.ts` because the purity
465
+ gate's layer rule says `wiring/` may not take imports from `cli/`. The parity
466
+ scanner works the same way: `cliDispatchTokens()` gets the live token list
467
+ straight from the registry's CLI capabilities (`cli.caseTokens` when a row
468
+ names more than one first token, else `cli.path`), then makes sure each
469
+ declared path resolves through the live dispatcher — there is no grep over
470
+ built dispatch source.
445
471
 
446
472
  `test/cli-mcp-parity-smoke.js` proves the contract from end to end. It checks
447
473
  registry ⇄ CLI ⇄ MCP coverage (every declared capability is found on its declared
@@ -638,3 +664,7 @@ CLI golden-path fixes: `cw -q "…"` routes the question (was read as an app id
638
664
  0.2.2
639
665
 
640
666
  0.2.3
667
+
668
+ 0.2.4
669
+
670
+ 0.2.5
@@ -181,3 +181,7 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
181
181
  0.2.2
182
182
 
183
183
  0.2.3
184
+
185
+ 0.2.4
186
+
187
+ 0.2.5
@@ -66,6 +66,40 @@ Additive: `RunQueueEntry` gets the optional `attempts`/`leaseId`/`leaseExpiresAt
66
66
  `queue add|list|drain|show` verbs that are already there do not change. No new database, no
67
67
  daemon-owned state.
68
68
 
69
+ ## Scheduled Tasks In Practice
70
+
71
+ CW scheduled tasks give looping prompts, cron-like schedules, one-shot
72
+ reminders, expiration, jitter, and clear completion. Schedules live in
73
+ `.cw/schedules/tasks.json`.
74
+
75
+ ```bash
76
+ # a /loop-ready schedule (cw loop is the short form of schedule create --kind loop)
77
+ cw loop --intervalMinutes 30 --prompt "Check this workflow and continue if work is due."
78
+
79
+ # a cron schedule
80
+ cw schedule create --kind cron --cron "*/15 * * * *" --prompt "Run the due workflow scan."
81
+
82
+ # a one-shot reminder
83
+ cw schedule create --kind reminder --delayMinutes 60 --prompt "Remind me to inspect the report."
84
+
85
+ # list, inspect, and manage
86
+ cw schedule list
87
+ cw schedule due
88
+ cw schedule complete <schedule-id>
89
+ cw schedule pause <schedule-id> / resume <schedule-id> / run-now <schedule-id>
90
+ cw schedule history <schedule-id>
91
+ cw schedule delete <schedule-id>
92
+
93
+ # the local daemon: one sweep, or a loop
94
+ cw schedule daemon --once
95
+ cw schedule daemon --intervalSeconds 60
96
+ ```
97
+
98
+ Notes: time is measured to the minute; expiration defaults to 7 days;
99
+ `jitterSeconds` can put space between runs. CW does not start the daemon by
100
+ default — use `schedule daemon`, cron, or another overseer to call
101
+ `schedule due` and run due prompts.
102
+
69
103
  ## See Also
70
104
 
71
105
  run-registry-control-plane(7), cli-mcp-parity(7), release-and-migration(7)
@@ -165,3 +199,7 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
165
199
  0.2.2
166
200
 
167
201
  0.2.3
202
+
203
+ 0.2.4
204
+
205
+ 0.2.5
@@ -3,8 +3,15 @@
3
3
  CW adds `cw ledger` — a way for two agents scoped to two separate repos to hand
4
4
  each other a CHANGE PROPOSAL or a REVIEW VERDICT as verifiable data, not chat.
5
5
  One side proposes or reviews; the other side verifies the entry fail-closed and
6
- turns a proposal into a real pull request. Design notes:
7
- [handoff-ledger](designs/handoff-ledger.md).
6
+ turns a proposal into a real pull request.
7
+
8
+ Design note (why not a shared folder): the obvious first design — a shared
9
+ local directory both agents read and append to — only works when both agents
10
+ run on ONE machine with ONE filesystem. Two agents scoped to two repos (or two
11
+ machines) need a transport both can already reach, with saved, inspectable,
12
+ fail-closed state — never a fabricated hand-off. That is why an entry is a
13
+ self-verifying file in a git repo both sides can push and pull, and why the
14
+ kernel holds no git logic at all.
8
15
 
9
16
  Each entry is a self-contained JSON object that carries its own sha256 content
10
17
  digest. The producing side prints one; it reaches the other session by human
@@ -51,13 +58,18 @@ cw ledger review --from <a> --to <b> --target <proposal-id|pr-ref> \
51
58
  --verdict <approved|rejected> [--findings "a,b"]
52
59
  cw ledger verify [--file <path>] # else reads the entry from stdin
53
60
  cw ledger apply [--file <path>] # verify a proposal, then print its diff
54
- cw ledger list --dir <ledger-dir> [--dir <mirror-2> ...] # verify a dir (or union of mirrors)
61
+ cw ledger list --ledger-dir <ledger-dir> [--ledger-dir <mirror-2> ...] # verify a dir (or union of mirrors)
55
62
  ```
56
63
 
57
64
  All write JSON to stdout (stdout is data). `propose` and `review` print a sealed
58
65
  entry; `verify` prints a check report; `apply` prints a verify-plus-diff report;
59
66
  `list` prints a per-entry report over a directory.
60
67
 
68
+ `--ledger-dir` is the preferred spelling for `list`: the CLI front door
69
+ treats `--dir` as a global alias of `--repo` on every command, so the old
70
+ `--dir` made one flag mean two things here. `--dir` keeps working
71
+ unchanged as the legacy alias; when both are given, `--ledger-dir` wins.
72
+
61
73
  ## Applying a proposal — fail-closed
62
74
 
63
75
  A proposal carries a `suggestedDiff`, but a proposal never mutates the target
@@ -131,12 +143,12 @@ on the mirror-union output and on the `cw_ledger_list` MCP tool.
131
143
 
132
144
  ### Mirrors — union-verifying several directories
133
145
 
134
- `--dir` is repeatable. With two or more, `cw ledger list` **union-verifies** the
146
+ `--ledger-dir` (and the legacy `--dir`) is repeatable. With two or more, `cw ledger list` **union-verifies** the
135
147
  directories as mirrors of one ledger (e.g. the same handoff repo cloned from a
136
148
  GitHub remote and one or more self-hosted Gitea remotes in different places):
137
149
 
138
150
  ```
139
- cw ledger list --dir gh/ledger --dir gitea-eu/ledger --dir gitea-asia/ledger
151
+ cw ledger list --ledger-dir gh/ledger --ledger-dir gitea-eu/ledger --ledger-dir gitea-asia/ledger
140
152
  ```
141
153
 
142
154
  The union is **conflict-free by construction**: entries are immutable and
@@ -146,10 +158,10 @@ across mirrors** — a tampered entry in ANY mirror sets `allOk:false` and exits
146
158
  `1`. This is for redundancy and reachability, not load: the ledger's traffic is
147
159
  tiny; multiple hosts guard against one being down or unreachable.
148
160
 
149
- A single `--dir` keeps the original single-directory output (a `dir` field, no
161
+ A single directory flag keeps the original single-directory output (a `dir` field, no
150
162
  `dirs`); two or more switch to the union shape (`dirs` plus a per-entry `dirs`).
151
163
  The transport stays git-host-agnostic — adding a mirror is one more clone + one
152
- more `--dir`, no code change.
164
+ more `--ledger-dir`, no code change.
153
165
 
154
166
  ## Entry shape
155
167
 
@@ -214,4 +226,4 @@ cw ledger verify --file verdict.json
214
226
  Stage 1 shipped the CLI verbs (human relay). Stage 2 adds the MCP surface and
215
227
  the git-as-ledger transport (`cw ledger list` over a shared repo). Still open:
216
228
  the operator creates the shared handoff repo and scopes both agent environments
217
- into it. See [handoff-ledger](designs/handoff-ledger.md).
229
+ into it. Setup runbook: [handoff-setup](handoff-setup.md).
@@ -184,3 +184,7 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
184
184
  0.2.2
185
185
 
186
186
  0.2.3
187
+
188
+ 0.2.4
189
+
190
+ 0.2.5
@@ -325,3 +325,7 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
325
325
  0.2.2
326
326
 
327
327
  0.2.3
328
+
329
+ 0.2.4
330
+
331
+ 0.2.5
@@ -355,3 +355,7 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
355
355
  0.2.2
356
356
 
357
357
  0.2.3
358
+
359
+ 0.2.4
360
+
361
+ 0.2.5
package/docs/fix.7.md CHANGED
@@ -8,7 +8,6 @@
8
8
 
9
9
  ```text
10
10
  node dist/cli.js fix
11
- node dist/cli.js fix --json
12
11
  ```
13
12
 
14
13
  ## DESCRIPTION
@@ -28,9 +27,10 @@ If any check has status `fail`, the command exits with code 1.
28
27
 
29
28
  ## OPTIONS
30
29
 
31
- `--json`
32
- : Give back the full doctor report as a stable JSON object, with the same shape
33
- as `cw doctor --json`. The `checks` array carries every fix string.
30
+ `cw fix` takes no options of its own; its output is always the plain fix-command
31
+ text (a `--json` flag is silently ignored this byte behavior is pinned by
32
+ `v2/conformance/cases/report-fix.case.js`). For a machine-readable report of the
33
+ same checks, use `cw doctor --json`: its `checks` array carries every fix string.
34
34
 
35
35
  ## EXIT CODES
36
36