cool-workflow 0.1.83 → 0.1.84

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 (53) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/README.md +4 -0
  4. package/apps/architecture-review/app.json +1 -1
  5. package/apps/architecture-review-fast/app.json +1 -1
  6. package/apps/end-to-end-golden-path/app.json +1 -1
  7. package/apps/pr-review-fix-ci/app.json +1 -1
  8. package/apps/release-cut/app.json +1 -1
  9. package/apps/research-synthesis/app.json +1 -1
  10. package/dist/capability-registry.js +262 -0
  11. package/dist/cli/command-surface.js +1320 -0
  12. package/dist/cli.js +2 -1318
  13. package/dist/mcp-server.js +5 -1452
  14. package/dist/mcp-surface.js +1441 -0
  15. package/dist/version.js +1 -1
  16. package/dist/worker-accept/acceptance.js +114 -0
  17. package/dist/worker-accept/blackboard-fanout.js +80 -0
  18. package/dist/worker-accept/blackboard-linkage.js +19 -0
  19. package/dist/worker-accept/context.js +2 -0
  20. package/dist/worker-accept/telemetry-ledger.js +116 -0
  21. package/dist/worker-accept/validation.js +77 -0
  22. package/dist/worker-accept/verifier-completion.js +73 -0
  23. package/dist/worker-isolation.js +19 -444
  24. package/docs/agent-delegation-drive.7.md +4 -0
  25. package/docs/cli-mcp-parity.7.md +4 -0
  26. package/docs/contract-migration-tooling.7.md +4 -0
  27. package/docs/control-plane-scheduling.7.md +4 -0
  28. package/docs/durable-state-and-locking.7.md +4 -0
  29. package/docs/evidence-adoption-reasoning-chain.7.md +4 -0
  30. package/docs/execution-backends.7.md +4 -0
  31. package/docs/multi-agent-cli-mcp-surface.7.md +4 -0
  32. package/docs/multi-agent-eval-replay-harness.7.md +4 -0
  33. package/docs/multi-agent-operator-ux.7.md +4 -0
  34. package/docs/node-snapshot-diff-replay.7.md +4 -0
  35. package/docs/observability-cost-accounting.7.md +4 -0
  36. package/docs/project-index.md +11 -5
  37. package/docs/real-execution-backends.7.md +4 -0
  38. package/docs/release-and-migration.7.md +4 -0
  39. package/docs/release-tooling.7.md +14 -1
  40. package/docs/run-registry-control-plane.7.md +4 -0
  41. package/docs/run-retention-reclamation.7.md +4 -0
  42. package/docs/state-explosion-management.7.md +4 -0
  43. package/docs/team-collaboration.7.md +4 -0
  44. package/docs/web-desktop-workbench.7.md +4 -0
  45. package/manifest/plugin.manifest.json +1 -1
  46. package/package.json +1 -1
  47. package/scripts/bump-version.js +9 -1
  48. package/scripts/canonical-apps.js +4 -4
  49. package/scripts/dogfood-release.js +1 -1
  50. package/scripts/golden-path.js +4 -4
  51. package/scripts/parity-check.js +27 -57
  52. package/scripts/release-flow.js +7 -6
  53. package/scripts/sync-project-index.js +1 -1
@@ -1,16 +1,16 @@
1
1
  # Cool Workflow Project Index
2
2
 
3
- Generated from the current repository code on 2026-06-16 by `npm run sync:project-index`.
3
+ Generated from the current repository code on 2026-06-17 by `npm run sync:project-index`.
4
4
 
5
5
  ## Snapshot
6
6
 
7
7
  - Package: `cool-workflow`
8
- - Version: `0.1.83`
9
- - Source modules: `61`
8
+ - Version: `0.1.84`
9
+ - Source modules: `62`
10
10
  - Workflow apps: `7`
11
11
  - Docs: `49`
12
- - Smoke tests: `92`
13
- - Repository: http://local_proxy@127.0.0.1:33273/git/coo1white/cool-workflow
12
+ - Smoke tests: `97`
13
+ - Repository: https://github.com/coo1white/cool-workflow
14
14
 
15
15
  ## Architecture
16
16
 
@@ -92,6 +92,7 @@ multi-agent host -> topology -> blackboard/coordinator
92
92
  - [evidence-reasoning.ts](../src/evidence-reasoning.ts)
93
93
  - [execution-backend.ts](../src/execution-backend.ts)
94
94
  - [gates.ts](../src/gates.ts)
95
+ - [mcp-surface.ts](../src/mcp-surface.ts)
95
96
  - [multi-agent-eval.ts](../src/multi-agent-eval.ts)
96
97
  - [multi-agent-operator-ux.ts](../src/multi-agent-operator-ux.ts)
97
98
  - [multi-agent-trust.ts](../src/multi-agent-trust.ts)
@@ -189,9 +190,11 @@ Smoke tests mirror the public contracts. The high-signal suites are:
189
190
  - [audit-verify-smoke.js](../test/audit-verify-smoke.js)
190
191
  - [backend-registry-smoke.js](../test/backend-registry-smoke.js)
191
192
  - [block-unapproved-tag-smoke.js](../test/block-unapproved-tag-smoke.js)
193
+ - [bump-version-idempotent-smoke.js](../test/bump-version-idempotent-smoke.js)
192
194
  - [candidate-scoring-smoke.js](../test/candidate-scoring-smoke.js)
193
195
  - [canonical-workflow-apps-smoke.js](../test/canonical-workflow-apps-smoke.js)
194
196
  - [claude-p-agent-wrapper-smoke.js](../test/claude-p-agent-wrapper-smoke.js)
197
+ - [cli-command-surface-smoke.js](../test/cli-command-surface-smoke.js)
195
198
  - [cli-jsonmode-parity-smoke.js](../test/cli-jsonmode-parity-smoke.js)
196
199
  - [cli-mcp-parity-smoke.js](../test/cli-mcp-parity-smoke.js)
197
200
  - [concurrent-failure-semantics-smoke.js](../test/concurrent-failure-semantics-smoke.js)
@@ -213,6 +216,7 @@ Smoke tests mirror the public contracts. The high-signal suites are:
213
216
  - [freebsd-audit-fixes-smoke.js](../test/freebsd-audit-fixes-smoke.js)
214
217
  - [h7-custom-profile-persist-smoke.js](../test/h7-custom-profile-persist-smoke.js)
215
218
  - [mcp-app-surface-smoke.js](../test/mcp-app-surface-smoke.js)
219
+ - [mcp-surface-registry-smoke.js](../test/mcp-surface-registry-smoke.js)
216
220
  - [multi-agent-cli-mcp-surface-smoke.js](../test/multi-agent-cli-mcp-surface-smoke.js)
217
221
  - [multi-agent-eval-determinism-regression-smoke.js](../test/multi-agent-eval-determinism-regression-smoke.js)
218
222
  - [multi-agent-eval-replay-harness-smoke.js](../test/multi-agent-eval-replay-harness-smoke.js)
@@ -228,6 +232,7 @@ Smoke tests mirror the public contracts. The high-signal suites are:
228
232
  - [operator-ux-smoke.js](../test/operator-ux-smoke.js)
229
233
  - [parallel-onramp-smoke.js](../test/parallel-onramp-smoke.js)
230
234
  - [parity-doc-sync-smoke.js](../test/parity-doc-sync-smoke.js)
235
+ - [pii-redaction-smoke.js](../test/pii-redaction-smoke.js)
231
236
  - [pipeline-auto-advance-smoke.js](../test/pipeline-auto-advance-smoke.js)
232
237
  - [pipeline-runner-smoke.js](../test/pipeline-runner-smoke.js)
233
238
  - [project-index-sync-smoke.js](../test/project-index-sync-smoke.js)
@@ -271,6 +276,7 @@ Smoke tests mirror the public contracts. The high-signal suites are:
271
276
  - [verifier-gated-commit-smoke.js](../test/verifier-gated-commit-smoke.js)
272
277
  - [verify-import-audit-chain-smoke.js](../test/verify-import-audit-chain-smoke.js)
273
278
  - [web-desktop-workbench-smoke.js](../test/web-desktop-workbench-smoke.js)
279
+ - [worker-accept-path-architecture-smoke.js](../test/worker-accept-path-architecture-smoke.js)
274
280
  - [worker-isolation-smoke.js](../test/worker-isolation-smoke.js)
275
281
  - [worker-retry-count-smoke.js](../test/worker-retry-count-smoke.js)
276
282
  - [workflow-app-framework-smoke.js](../test/workflow-app-framework-smoke.js)
@@ -153,3 +153,7 @@ _No behavioral change in v0.1.82 (delegated child programs moved out to `scripts
153
153
  ## Hardening and Onboarding (v0.1.83)
154
154
 
155
155
  Loaders fail closed on corrupt state; store writes are made safe under more than one writer; a new cw doctor checks your setup; help lists every command; and the docs are put into Basic English.
156
+
157
+ ## Privacy Release (v0.1.84)
158
+
159
+ No other change to this page in v0.1.84.
@@ -293,3 +293,7 @@ _No changes in v0.1.82._
293
293
  ## Hardening and Onboarding (v0.1.83)
294
294
 
295
295
  Loaders fail closed on corrupt state; store writes are made safe under more than one writer; a new cw doctor checks your setup; help lists every command; and the docs are put into Basic English.
296
+
297
+ ## Privacy Release (v0.1.84)
298
+
299
+ No other change to this page in v0.1.84.
@@ -143,12 +143,21 @@ The per-platform difference is config, not code — set the reviewer agent here:
143
143
 
144
144
  | Platform | Reviewer config |
145
145
  |---|---|
146
- | Claude | `CW_AGENT_COMMAND="claude -p {{input}}"` |
146
+ | Claude | `CW_AGENT_COMMAND="claude -p --permission-mode acceptEdits {{input}}"` |
147
147
  | Codex | `CW_AGENT_COMMAND="codex exec {{input}}"` |
148
148
  | Gemini | `CW_AGENT_COMMAND="gemini -p {{input}}"` |
149
149
  | OpenCode | `CW_AGENT_COMMAND="opencode run -m <provider/model> {{input}}"` |
150
150
  | DeepSeek | via OpenCode (`-m deepseek/deepseek-chat`) or `CW_AGENT_ENDPOINT=<deepseek-compatible HTTP agent>` |
151
151
 
152
+ The reviewer's last act is to **write** the verdict file, so a headless agent
153
+ needs file-write permission. For `claude -p`, recent CLIs default to a mode that
154
+ silently denies `Write` (no prompt in headless mode), so the reviewer reaches a
155
+ verdict but cannot persist it and the flow fails closed at `[3/3] verify` with
156
+ `no verdict written`. The `--permission-mode acceptEdits` flag in the preset above
157
+ fixes this; Read/Bash (which the review itself needs) keep working. The reviewer
158
+ CLI must also be logged in (`claude auth login` / `claude auth status` →
159
+ `loggedIn: true`) — a fresh shell or CI runner is not.
160
+
152
161
  `{{input}}` is put in place of the reviewer prompt file path. Gemini and OpenCode
153
162
  also get generated MCP manifests (`.gemini-plugin/`, `.opencode-plugin/`) so the
154
163
  `cw_*` tools are there as MCP tools in those hosts. The verdict path
@@ -200,3 +209,7 @@ _No changes to the release-tooling contract in v0.1.82._
200
209
  ## Hardening and Onboarding (v0.1.83)
201
210
 
202
211
  Loaders fail closed on corrupt state; store writes are made safe under more than one writer; a new cw doctor checks your setup; help lists every command; and the docs are put into Basic English.
212
+
213
+ ## Privacy Release (v0.1.84)
214
+
215
+ `release-flow` now writes reviewer input with repo-local paths, so local user home names do not enter the saved review prompt.
@@ -404,3 +404,7 @@ _No behavioral change in v0.1.82 (run resolution now threads an explicit base di
404
404
  ## Hardening and Onboarding (v0.1.83)
405
405
 
406
406
  Loaders fail closed on corrupt state; store writes are made safe under more than one writer; a new cw doctor checks your setup; help lists every command; and the docs are put into Basic English.
407
+
408
+ ## Privacy Release (v0.1.84)
409
+
410
+ No other change to this page in v0.1.84.
@@ -204,3 +204,7 @@ _No changes in v0.1.82._
204
204
  ## Hardening and Onboarding (v0.1.83)
205
205
 
206
206
  Loaders fail closed on corrupt state; store writes are made safe under more than one writer; a new cw doctor checks your setup; help lists every command; and the docs are put into Basic English.
207
+
208
+ ## Privacy Release (v0.1.84)
209
+
210
+ No other change to this page in v0.1.84.
@@ -282,3 +282,7 @@ _No changes in v0.1.82._
282
282
  ## Hardening and Onboarding (v0.1.83)
283
283
 
284
284
  Loaders fail closed on corrupt state; store writes are made safe under more than one writer; a new cw doctor checks your setup; help lists every command; and the docs are put into Basic English.
285
+
286
+ ## Privacy Release (v0.1.84)
287
+
288
+ No other change to this page in v0.1.84.
@@ -218,3 +218,7 @@ _No changes in v0.1.82._
218
218
  ## Hardening and Onboarding (v0.1.83)
219
219
 
220
220
  Loaders fail closed on corrupt state; store writes are made safe under more than one writer; a new cw doctor checks your setup; help lists every command; and the docs are put into Basic English.
221
+
222
+ ## Privacy Release (v0.1.84)
223
+
224
+ No other change to this page in v0.1.84.
@@ -226,3 +226,7 @@ _No changes in v0.1.82._
226
226
  ## Hardening and Onboarding (v0.1.83)
227
227
 
228
228
  Loaders fail closed on corrupt state; store writes are made safe under more than one writer; a new cw doctor checks your setup; help lists every command; and the docs are put into Basic English.
229
+
230
+ ## Privacy Release (v0.1.84)
231
+
232
+ No other change to this page in v0.1.84.
@@ -2,7 +2,7 @@
2
2
  "_comment": "SINGLE SOURCE OF TRUTH for every vendor manifest. Edit THIS file, then run `npm run gen:manifests`. Do NOT hand-edit the generated vendor manifests (.claude-plugin/, .codex-plugin/, .agents/, .mcp.json) — `npm run gen:manifests -- --check` (run by release:check) will fail if they drift from this source.",
3
3
  "identity": {
4
4
  "name": "cool-workflow",
5
- "version": "0.1.83",
5
+ "version": "0.1.84",
6
6
  "license": "BSD-2-Clause",
7
7
  "homepage": "https://github.com/coo1white/cool-workflow",
8
8
  "author": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cool-workflow",
3
- "version": "0.1.83",
3
+ "version": "0.1.84",
4
4
  "bin": {
5
5
  "cool-workflow": "scripts/cw.js",
6
6
  "cw": "scripts/cw.js"
@@ -59,7 +59,15 @@ function main() {
59
59
  if (!SEMVER.test(next)) fail(`"${next}" is not a x.y.z version`);
60
60
 
61
61
  const current = JSON.parse(fs.readFileSync(path.join(pluginRoot, "package.json"), "utf8")).version;
62
- if (next === current) fail(`already at ${next}`);
62
+ if (next === current) {
63
+ // Idempotent: re-running the bump for the same version is a no-op (exit 0)
64
+ // rather than a hard fail. Required by `release-flow --cut --version X` when
65
+ // the bump was already landed in a prior PR (e.g. a release-prep PR that
66
+ // committed the version surfaces, then a follow-up fix on top — cut should
67
+ // still be able to commit the verdict and tag without re-bumping).
68
+ process.stdout.write(`bump:version: already at ${next}, no surfaces to update\n`);
69
+ process.exit(0);
70
+ }
63
71
 
64
72
  const touched = [];
65
73
  const note = (rel) => touched.push(rel);
@@ -83,7 +83,7 @@ const canonicalApps = [
83
83
  "--source",
84
84
  "plugins/cool-workflow/docs/workflow-app-framework.7.md",
85
85
  "--scope",
86
- "Cool Workflow v0.1.83",
86
+ "Cool Workflow v0.1.84",
87
87
  "--freshness",
88
88
  "as of release preparation"
89
89
  ]
@@ -117,14 +117,14 @@ function main() {
117
117
  assert.ok(summary, `${app.id} must appear in app list`);
118
118
  assert.equal(summary.sourceKind, "app-directory");
119
119
  assert.equal(summary.legacy, false);
120
- assert.equal(summary.version, "0.1.83");
120
+ assert.equal(summary.version, "0.1.84");
121
121
 
122
122
  const validation = runJson(["app", "validate", manifestPath]);
123
123
  assert.equal(validation.valid, true, `${app.id} manifest must validate`);
124
124
 
125
125
  const shown = runJson(["app", "show", app.id]);
126
126
  assert.equal(shown.app.id, app.id);
127
- assert.equal(shown.app.version, "0.1.83");
127
+ assert.equal(shown.app.version, "0.1.84");
128
128
  assert.ok(shown.app.metadata.canonical, `${app.id} must be marked canonical`);
129
129
  assert.ok(shown.app.sandboxProfiles.length > 0, `${app.id} must declare sandbox profiles`);
130
130
  assertTaskIdsUnique(shown);
@@ -135,7 +135,7 @@ function main() {
135
135
  const plan = runJson(["plan", app.id, ...app.args(workspace)]);
136
136
  const state = JSON.parse(fs.readFileSync(plan.statePath, "utf8"));
137
137
  assert.equal(state.workflow.app.id, app.id);
138
- assert.equal(state.workflow.app.version, "0.1.83");
138
+ assert.equal(state.workflow.app.version, "0.1.84");
139
139
  assert.equal(state.workflow.app.metadata.canonical, true);
140
140
  assert.ok(state.tasks.some((task) => task.requiresEvidence), `${app.id} plan must include evidence gates`);
141
141
  assert.ok(state.tasks.every((task) => task.sandboxProfileId), `${app.id} plan must include sandbox hints`);
@@ -5,7 +5,7 @@ const { spawnSync } = require("node:child_process");
5
5
  const fs = require("node:fs");
6
6
  const path = require("node:path");
7
7
 
8
- const TARGET_VERSION = "0.1.83";
8
+ const TARGET_VERSION = "0.1.84";
9
9
  const PREVIOUS_VERSION = "0.1.31";
10
10
  const pluginRoot = path.resolve(__dirname, "..");
11
11
  const repoRoot = path.resolve(pluginRoot, "..", "..");
@@ -33,7 +33,7 @@ function main() {
33
33
  const appValidation = runJson(["app", "validate", "end-to-end-golden-path"], pluginRoot);
34
34
  assert.equal(appValidation.valid, true);
35
35
  assert.equal(appValidation.summary.id, "end-to-end-golden-path");
36
- assert.equal(appValidation.summary.version, "0.1.83");
36
+ assert.equal(appValidation.summary.version, "0.1.84");
37
37
 
38
38
  const plan = runJson(
39
39
  [
@@ -42,7 +42,7 @@ function main() {
42
42
  "--repo",
43
43
  tmp,
44
44
  "--question",
45
- "Prove the deterministic v0.1.83 end-to-end golden path."
45
+ "Prove the deterministic v0.1.84 end-to-end golden path."
46
46
  ],
47
47
  pluginRoot
48
48
  );
@@ -52,7 +52,7 @@ function main() {
52
52
 
53
53
  let state = readJson(plan.statePath);
54
54
  assert.equal(state.workflow.app.id, "end-to-end-golden-path");
55
- assert.equal(state.workflow.app.version, "0.1.83");
55
+ assert.equal(state.workflow.app.version, "0.1.84");
56
56
  assert.equal(state.loopStage, "interpret");
57
57
 
58
58
  const dispatch = runJson(["dispatch", plan.runId, "--limit", "1", "--sandbox", "readonly"], tmp);
@@ -195,7 +195,7 @@ function main() {
195
195
  assert.equal(reportPath, plan.reportPath);
196
196
  assert.ok(fs.existsSync(reportPath));
197
197
  const report = fs.readFileSync(reportPath, "utf8");
198
- assert.match(report, /Workflow App: end-to-end-golden-path@0\.1\.83/);
198
+ assert.match(report, /Workflow App: end-to-end-golden-path@0\.1\.84/);
199
199
  assert.match(report, /## Candidates/);
200
200
  assert.match(report, /## Trust Audit/);
201
201
  assert.match(report, /## Acceptance Rationale/);
@@ -9,8 +9,8 @@
9
9
  // FAIL CLOSED ON DRIFT (BSD discipline, same shape as gen-manifests --check):
10
10
  // 1. STATIC parity — the declared capability registry must exactly match the
11
11
  // live MCP tool list (tools/list) and the CLI dispatch tokens parsed from
12
- // dist/cli.js. A tool or command on one surface but not the other, or not
13
- // declared in src/capability-registry.ts, is release-blocking.
12
+ // the built CLI dispatch surface. A tool or command on one surface but not
13
+ // the other, or not declared in src/capability-registry.ts, is release-blocking.
14
14
  // 2. PAYLOAD parity — for every capability declared `payloadIdentical`, the
15
15
  // `cw <cmd> --json` payload must equal the `cw_<tool>` MCP result on a real
16
16
  // bootstrap run (whitespace + generation-moment ISO timestamps aside).
@@ -31,50 +31,16 @@ const cli = path.join(pluginRoot, "dist", "cli.js");
31
31
  const mcpServer = path.join(pluginRoot, "dist", "mcp-server.js");
32
32
  const registry = require(path.join(pluginRoot, "dist", "capability-registry.js"));
33
33
 
34
- // Read capabilities that are safe to probe on a freshly planned run with only a
35
- // runId (or no args). Each: [capability, cliArgvAfterRunId, mcpTool].
36
- // runId-less reads use [] for cli args and {} mcp args.
37
- const RUN_PROBES = [
38
- ["status", ["--json"], "cw_status"],
39
- ["operator.status", ["--json"], "cw_operator_status"],
40
- ["operator.report", ["--json"], "cw_operator_report"],
41
- ["graph", ["--json"], "cw_operator_graph"],
42
- ["report", ["--json"], "cw_report"],
43
- ["next", [], "cw_next"],
44
- ["state.check", [], "cw_state_check"],
45
- ["contract.show", [], "cw_contract_show"],
46
- ["node.list", [], "cw_node_list"],
47
- ["node.graph", ["--json"], "cw_node_graph"],
48
- ["worker.summary", ["--json"], "cw_worker_summary"],
49
- ["candidate.summary", ["--json"], "cw_candidate_summary"],
50
- ["feedback.summary", ["--json"], "cw_feedback_summary"],
51
- ["commit.summary", ["--json"], "cw_commit_summary"],
52
- ["audit.summary", [], "cw_audit_summary"],
53
- ["multi-agent.summary", ["--json"], "cw_multi_agent_summary"],
54
- ["workbench.view", ["--json"], "cw_workbench_view"],
55
- ["metrics.show", ["--json"], "cw_metrics_show"],
56
- ["review.status", ["--json"], "cw_review_status"],
57
- ["comment.list", ["--json"], "cw_comment_list"],
58
- ["run.drive", ["--json"], "cw_run_drive"],
59
- ["gc.plan", ["--json"], "cw_gc_plan"],
60
- ["gc.verify", ["--json"], "cw_gc_verify"]
61
- ];
62
- const GLOBAL_PROBES = [
63
- ["list", "cw_list"],
64
- ["app.list", "cw_app_list"],
65
- ["topology.list", "cw_topology_list"],
66
- ["sandbox.list", "cw_sandbox_list"],
67
- ["backend.list", "cw_backend_list"],
68
- ["backend.agent.config.show", "cw_backend_agent_config_show"],
69
- ["metrics.summary", "cw_metrics_summary"]
70
- ];
71
-
72
34
  function capById(id) {
73
35
  const cap = registry.CAPABILITY_REGISTRY.find((entry) => entry.capability === id);
74
36
  assert.ok(cap, `probe references unknown capability ${id}`);
75
37
  return cap;
76
38
  }
77
39
 
40
+ function jsonFlag(cap) {
41
+ return cap.cli.jsonMode === "flag" ? ["--json"] : [];
42
+ }
43
+
78
44
  // ---- 1. static surface parity ---------------------------------------------
79
45
  function liveMcpTools() {
80
46
  const result = execFileSync(node, [mcpServer], {
@@ -91,10 +57,14 @@ function liveMcpTools() {
91
57
  }
92
58
 
93
59
  function cliDispatchTokens() {
94
- const source = fs.readFileSync(cli, "utf8");
60
+ const source = cliDispatchSources().map((file) => fs.readFileSync(file, "utf8")).join("\n");
95
61
  return [...new Set([...source.matchAll(/case\s+"([^"]+)":/g)].map((match) => match[1]))];
96
62
  }
97
63
 
64
+ function cliDispatchSources() {
65
+ return [cli, path.join(pluginRoot, "dist", "cli", "command-surface.js")].filter((file) => fs.existsSync(file));
66
+ }
67
+
98
68
  // ---- 2. payload identity ---------------------------------------------------
99
69
  // Generation-moment ISO timestamps are presentation metadata, not capability
100
70
  // data: the same field carries the wall-clock instant of the render. Neutralize
@@ -144,31 +114,28 @@ async function payloadParity() {
144
114
  const runId = plan.runId;
145
115
  const mismatches = [];
146
116
  const checked = [];
117
+ const probePlan = registry.payloadProbePlan();
118
+ assert.deepEqual(probePlan.unclassified, [], "payload probe classification has unclassified capabilities");
119
+ assert.deepEqual(probePlan.duplicateClassifications, [], "payload probe classification has duplicates");
120
+ assert.deepEqual(probePlan.invalidClassifications, [], "payload probe classification names non-payload capabilities");
147
121
  const mcp = openMcp();
148
122
  try {
149
123
  await mcp.rpc("initialize", {});
150
- for (const [capability, mcpTool] of GLOBAL_PROBES) {
151
- const cap = capById(capability);
152
- assert.equal(cap.mcp.tool, mcpTool, `probe/registry MCP tool mismatch for ${capability}`);
124
+ for (const target of registry.payloadProbeTargets()) {
125
+ const cap = capById(target.capability);
153
126
  // jsonMode is the single source for the CLI's --json policy; this probe only
154
127
  // appends --json for "flag" verbs and JSON.parse-es the result. The human
155
128
  // rendering and "default"-verb no-flag JSON are pinned to cap.cli.jsonMode by
156
129
  // the companion test/cli-jsonmode-parity-smoke.js, so cli.ts can't silently
157
130
  // re-encode that policy by hand and drift from this registry data.
158
- const cliArgv = [...cap.cli.path, ...(cap.cli.jsonMode === "flag" ? ["--json"] : [])];
159
- const cliOut = JSON.parse(execFileSync(node, [cli, ...cliArgv], { cwd: workspace, encoding: "utf8" }));
160
- const mcpOut = await mcp.tool(mcpTool, { cwd: workspace });
161
- checked.push(capability);
162
- if (canonical(cliOut) !== canonical(mcpOut)) mismatches.push(capability);
163
- }
164
- for (const [capability, extraArgv, mcpTool] of RUN_PROBES) {
165
- const cap = capById(capability);
166
- assert.equal(cap.mcp.tool, mcpTool, `probe/registry MCP tool mismatch for ${capability}`);
167
- const cliArgv = [...cap.cli.path, runId, ...extraArgv];
131
+ const cliArgv = target.kind === "run"
132
+ ? [...cap.cli.path, runId, ...jsonFlag(cap)]
133
+ : [...cap.cli.path, ...jsonFlag(cap)];
168
134
  const cliOut = JSON.parse(execFileSync(node, [cli, ...cliArgv], { cwd: workspace, encoding: "utf8" }));
169
- const mcpOut = await mcp.tool(mcpTool, { cwd: workspace, runId });
170
- checked.push(capability);
171
- if (canonical(cliOut) !== canonical(mcpOut)) mismatches.push(capability);
135
+ const mcpArgs = target.kind === "run" ? { cwd: workspace, runId } : { cwd: workspace };
136
+ const mcpOut = await mcp.tool(cap.mcp.tool, mcpArgs);
137
+ checked.push(target.capability);
138
+ if (canonical(cliOut) !== canonical(mcpOut)) mismatches.push(target.capability);
172
139
  }
173
140
  } finally {
174
141
  mcp.server.kill();
@@ -204,6 +171,9 @@ async function main() {
204
171
  if (report.missingCliTokens.length) lines.push(` - registry declares CLI tokens absent from dist/cli.js: ${report.missingCliTokens.join(", ")}`);
205
172
  if (report.undeclaredCliTokens.length) lines.push(` - dist/cli.js dispatches tokens not declared in the registry: ${report.undeclaredCliTokens.join(", ")}`);
206
173
  if (report.reasonlessExceptions.length) lines.push(` - surface-specific / payload-divergent capabilities missing a recorded reason: ${report.reasonlessExceptions.join(", ")}`);
174
+ if (report.payloadProbeUnclassified.length) lines.push(` - payload-identical capabilities neither probed nor deferred: ${report.payloadProbeUnclassified.join(", ")}`);
175
+ if (report.payloadProbeDuplicateClassifications.length) lines.push(` - payload probe duplicate classifications: ${report.payloadProbeDuplicateClassifications.join(", ")}`);
176
+ if (report.payloadProbeInvalidClassifications.length) lines.push(` - payload probe classifications for invalid capabilities: ${report.payloadProbeInvalidClassifications.join(", ")}`);
207
177
  if (report.registryLint.length) lines.push(` - registry lint: ${report.registryLint.join("; ")}`);
208
178
  if (payload.mismatches.length) lines.push(` - cw --json != cw_<tool> payload for: ${payload.mismatches.join(", ")}`);
209
179
  lines.push("Reconcile src/capability-registry.ts, cli.ts, and mcp-server.ts so both surfaces render one data source.\n");
@@ -124,6 +124,7 @@ function reviewerPromptBody() {
124
124
 
125
125
  function buildReviewerInput(resultPath) {
126
126
  const action = MODE_CUT ? `tag v${cutVersion || "<next>"}` : "check (no tag)";
127
+ const resultDisplay = path.relative(repoRoot, resultPath);
127
128
  return [
128
129
  reviewerPromptBody(),
129
130
  "",
@@ -133,11 +134,11 @@ function buildReviewerInput(resultPath) {
133
134
  `- Previous tag: ${PREV_TAG || "(none)"}`,
134
135
  `- Diff range: ${PREV_TAG ? `${PREV_TAG}..HEAD` : "(no previous tag)"}`,
135
136
  `- Proposed action: ${action}`,
136
- `- Repo root (run git here): ${repoRoot}`,
137
+ "- Repo root (run git here): .",
137
138
  "",
138
139
  "## Required output — write ONLY this file, nothing else",
139
140
  `Write your verdict to this exact path:`,
140
- ` ${resultPath}`,
141
+ ` ${resultDisplay}`,
141
142
  "First line MUST be exactly one of:",
142
143
  ` APPROVED ${HEAD}`,
143
144
  " <one concrete sentence: the user-visible capability this ships>",
@@ -174,10 +175,10 @@ function delegateReview(resultPath, inputPath) {
174
175
  }
175
176
 
176
177
  const subMap = {
177
- input: inputPath,
178
- manifest: inputPath,
179
- result: resultPath,
180
- workerDir: repoRoot,
178
+ input: path.relative(repoRoot, inputPath),
179
+ manifest: path.relative(repoRoot, inputPath),
180
+ result: path.relative(repoRoot, resultPath),
181
+ workerDir: ".",
181
182
  model: cfg.model || "",
182
183
  prompt: fs.readFileSync(inputPath, "utf8")
183
184
  };
@@ -311,7 +311,7 @@ function ensureLine(file, line, afterHeading) {
311
311
  }
312
312
 
313
313
  function detectObsidianVault() {
314
- const candidate = path.join(process.env.HOME || "", "Documents", "Nick");
314
+ const candidate = process.env.CW_OBSIDIAN_VAULT || path.join(process.env.HOME || "", "Documents", "Cool Workflow");
315
315
  return fs.existsSync(path.join(candidate, ".obsidian")) ? candidate : "";
316
316
  }
317
317