cool-workflow 0.2.0 → 0.2.2

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 (141) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/README.md +7 -5
  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 +20 -88
  12. package/dist/cli/parseargv.js +6 -3
  13. package/dist/core/capability-data.js +270 -0
  14. package/dist/core/capability-table.js +15 -3519
  15. package/dist/core/format/help.js +30 -7
  16. package/dist/core/hash.js +1 -1
  17. package/dist/core/multi-agent/candidate-scoring.js +1 -1
  18. package/dist/core/multi-agent/collaboration.js +3 -2
  19. package/dist/core/multi-agent/coordinator.js +4 -3
  20. package/dist/core/multi-agent/eval-replay.js +1 -1
  21. package/dist/core/multi-agent/runtime.js +2 -2
  22. package/dist/core/multi-agent/trust-policy.js +1 -1
  23. package/dist/core/pipeline/commit-gate.js +2 -1
  24. package/dist/core/pipeline/contract.js +1 -1
  25. package/dist/core/pipeline/drive-decide.js +1 -1
  26. package/dist/core/state/contract-migration.js +1 -1
  27. package/dist/core/state/migrations.js +2 -2
  28. package/dist/core/state/node-snapshot.js +1 -1
  29. package/dist/core/state/state-explosion/digest.js +3 -2
  30. package/dist/core/state/state-explosion/graph.js +13 -12
  31. package/dist/core/state/state-explosion/helpers.js +5 -4
  32. package/dist/core/state/state-explosion/report.js +1 -1
  33. package/dist/core/state/state-explosion/size.js +1 -1
  34. package/dist/core/state/state-node.js +2 -2
  35. package/dist/core/state/types.js +1 -1
  36. package/dist/core/trust/ledger.js +3 -2
  37. package/dist/core/trust/telemetry-attestation.js +3 -3
  38. package/dist/core/trust/telemetry-ledger.js +2 -2
  39. package/dist/core/types/execution-backend.js +18 -0
  40. package/dist/core/types/observability.js +7 -0
  41. package/dist/core/util/collate.js +23 -0
  42. package/dist/core/version.js +1 -1
  43. package/dist/core/workflow-apps/app-schema.js +1 -1
  44. package/dist/mcp/dispatch.js +1 -1
  45. package/dist/mcp/server.js +1 -1
  46. package/dist/shell/agent-config.js +1 -1
  47. package/dist/shell/audit-cli.js +46 -2
  48. package/dist/shell/commit-summary.js +2 -1
  49. package/dist/shell/doctor.js +17 -4
  50. package/dist/shell/drive.js +47 -19
  51. package/dist/shell/evidence-reasoning.js +4 -3
  52. package/dist/shell/execution-backend/agent.js +42 -10
  53. package/dist/shell/execution-backend/ci.js +1 -1
  54. package/dist/shell/execution-backend/container.js +1 -1
  55. package/dist/shell/execution-backend/envelopes.js +1 -1
  56. package/dist/shell/execution-backend/local.js +1 -1
  57. package/dist/shell/execution-backend/probes.js +1 -1
  58. package/dist/shell/execution-backend/registry.js +3 -2
  59. package/dist/shell/execution-backend/remote.js +1 -1
  60. package/dist/shell/execution-backend/types.js +0 -9
  61. package/dist/shell/fs-atomic.js +15 -2
  62. package/dist/shell/ledger-io.js +1 -1
  63. package/dist/shell/metrics-cli.js +4 -2
  64. package/dist/shell/multi-agent-host.js +1 -1
  65. package/dist/shell/multi-agent-operator-ux.js +4 -3
  66. package/dist/shell/observability.js +12 -11
  67. package/dist/shell/onramp.js +17 -1
  68. package/dist/shell/operator-ux-text.js +2 -1
  69. package/dist/shell/operator-ux.js +7 -6
  70. package/dist/shell/pipeline-cli.js +82 -72
  71. package/dist/shell/reclamation-io.js +3 -2
  72. package/dist/shell/registry-cli.js +15 -0
  73. package/dist/shell/report.js +2 -1
  74. package/dist/shell/run-registry-io.js +1 -1
  75. package/dist/shell/run-store.js +17 -0
  76. package/dist/shell/sandbox-profile.js +1 -1
  77. package/dist/shell/scheduler-io.js +46 -37
  78. package/dist/shell/scheduling-io.js +32 -22
  79. package/dist/shell/state-explosion-cli.js +2 -1
  80. package/dist/shell/telemetry-ledger-io.js +1 -1
  81. package/dist/shell/term.js +1 -1
  82. package/dist/shell/topology-io.js +2 -1
  83. package/dist/shell/trust-audit.js +45 -5
  84. package/dist/shell/workbench-host.js +9 -1
  85. package/dist/shell/workbench.js +6 -11
  86. package/dist/shell/worker-cli.js +9 -1
  87. package/dist/shell/worker-isolation.js +34 -9
  88. package/dist/shell/workflow-app-loader.js +3 -2
  89. package/dist/wiring/capability-table/basics.js +66 -0
  90. package/dist/wiring/capability-table/exec-backend.js +162 -0
  91. package/dist/wiring/capability-table/index.js +42 -0
  92. package/dist/wiring/capability-table/multi-agent.js +623 -0
  93. package/dist/wiring/capability-table/parity.js +466 -0
  94. package/dist/wiring/capability-table/pipeline.js +280 -0
  95. package/dist/wiring/capability-table/registry-core.js +189 -0
  96. package/dist/wiring/capability-table/reporting.js +394 -0
  97. package/dist/wiring/capability-table/scheduling-registry.js +558 -0
  98. package/dist/wiring/capability-table/state.js +173 -0
  99. package/dist/wiring/capability-table/trust-ledger.js +134 -0
  100. package/dist/wiring/capability-table/workflow-apps.js +366 -0
  101. package/docs/agent-delegation-drive.7.md +4 -0
  102. package/docs/cli-mcp-parity.7.md +9 -2
  103. package/docs/contract-migration-tooling.7.md +4 -0
  104. package/docs/control-plane-scheduling.7.md +4 -0
  105. package/docs/durable-state-and-locking.7.md +24 -0
  106. package/docs/evidence-adoption-reasoning-chain.7.md +4 -0
  107. package/docs/execution-backends.7.md +4 -0
  108. package/docs/index.md +1 -0
  109. package/docs/multi-agent-cli-mcp-surface.7.md +4 -0
  110. package/docs/multi-agent-eval-replay-harness.7.md +4 -0
  111. package/docs/multi-agent-operator-ux.7.md +4 -0
  112. package/docs/node-snapshot-diff-replay.7.md +4 -0
  113. package/docs/observability-cost-accounting.7.md +6 -1
  114. package/docs/project-index.md +30 -5
  115. package/docs/real-execution-backends.7.md +4 -0
  116. package/docs/release-and-migration.7.md +4 -0
  117. package/docs/release-tooling.7.md +4 -0
  118. package/docs/remote-source-review.7.md +4 -4
  119. package/docs/report-verifiable-bundle.7.md +1 -1
  120. package/docs/run-registry-control-plane.7.md +4 -0
  121. package/docs/run-retention-reclamation.7.md +4 -0
  122. package/docs/security-trust-hardening.7.md +33 -1
  123. package/docs/state-explosion-management.7.md +4 -0
  124. package/docs/team-collaboration.7.md +4 -0
  125. package/docs/trust-audit-anchor.7.md +69 -0
  126. package/docs/web-desktop-workbench.7.md +15 -1
  127. package/manifest/plugin.manifest.json +1 -1
  128. package/package.json +3 -1
  129. package/scripts/agents/agent-adapter-core.js +22 -2
  130. package/scripts/agents/claude-p-agent.js +8 -3
  131. package/scripts/agents/gemini-agent.js +5 -1
  132. package/scripts/agents/opencode-agent.js +5 -1
  133. package/scripts/bump-version.js +24 -2
  134. package/scripts/canonical-apps.js +4 -4
  135. package/scripts/dogfood-release.js +1 -1
  136. package/scripts/golden-path.js +4 -4
  137. package/scripts/purity-baseline.json +68 -0
  138. package/scripts/purity-gate.js +239 -0
  139. package/scripts/release-check.js +8 -1
  140. package/scripts/version-sync-check.js +33 -12
  141. package/workflows/README.md +19 -0
@@ -1,15 +1,15 @@
1
1
  # Cool Workflow Project Index
2
2
 
3
- Generated from the current repository code on 2026-07-05 by `npm run sync:project-index`.
3
+ Generated from the current repository code on 2026-07-08 by `npm run sync:project-index`.
4
4
 
5
5
  ## Snapshot
6
6
 
7
7
  - Package: `cool-workflow`
8
- - Version: `0.2.0`
9
- - Source modules: `129`
8
+ - Version: `0.2.2`
9
+ - Source modules: `145`
10
10
  - Workflow apps: `8`
11
- - Docs: `61`
12
- - Smoke tests: `173`
11
+ - Docs: `62`
12
+ - Smoke tests: `181`
13
13
  - Repository: https://github.com/coo1white/cool-workflow
14
14
 
15
15
  ## Architecture
@@ -82,6 +82,7 @@ multi-agent host -> topology -> blackboard/coordinator
82
82
  - [cli/entry.ts](../src/cli/entry.ts)
83
83
  - [cli/io.ts](../src/cli/io.ts)
84
84
  - [cli/parseargv.ts](../src/cli/parseargv.ts)
85
+ - [core/capability-data.ts](../src/core/capability-data.ts)
85
86
  - [core/capability-table.ts](../src/core/capability-table.ts)
86
87
  - [core/format/help.ts](../src/core/format/help.ts)
87
88
  - [core/format/state-explosion-text.ts](../src/core/format/state-explosion-text.ts)
@@ -113,6 +114,9 @@ multi-agent host -> topology -> blackboard/coordinator
113
114
  - [core/trust/telemetry-ledger.ts](../src/core/trust/telemetry-ledger.ts)
114
115
  - [core/types.ts](../src/core/types.ts)
115
116
  - [core/types/boundary.ts](../src/core/types/boundary.ts)
117
+ - [core/types/execution-backend.ts](../src/core/types/execution-backend.ts)
118
+ - [core/types/observability.ts](../src/core/types/observability.ts)
119
+ - [core/util/collate.ts](../src/core/util/collate.ts)
116
120
  - [mcp/dispatch.ts](../src/mcp/dispatch.ts)
117
121
  - [mcp/server.ts](../src/mcp/server.ts)
118
122
  - [shell/agent-config.ts](../src/shell/agent-config.ts)
@@ -181,6 +185,18 @@ multi-agent host -> topology -> blackboard/coordinator
181
185
  - [shell/workbench-text.ts](../src/shell/workbench-text.ts)
182
186
  - [shell/workbench.ts](../src/shell/workbench.ts)
183
187
  - [shell/worker-cli.ts](../src/shell/worker-cli.ts)
188
+ - [wiring/capability-table/basics.ts](../src/wiring/capability-table/basics.ts)
189
+ - [wiring/capability-table/exec-backend.ts](../src/wiring/capability-table/exec-backend.ts)
190
+ - [wiring/capability-table/index.ts](../src/wiring/capability-table/index.ts)
191
+ - [wiring/capability-table/multi-agent.ts](../src/wiring/capability-table/multi-agent.ts)
192
+ - [wiring/capability-table/parity.ts](../src/wiring/capability-table/parity.ts)
193
+ - [wiring/capability-table/pipeline.ts](../src/wiring/capability-table/pipeline.ts)
194
+ - [wiring/capability-table/registry-core.ts](../src/wiring/capability-table/registry-core.ts)
195
+ - [wiring/capability-table/reporting.ts](../src/wiring/capability-table/reporting.ts)
196
+ - [wiring/capability-table/scheduling-registry.ts](../src/wiring/capability-table/scheduling-registry.ts)
197
+ - [wiring/capability-table/state.ts](../src/wiring/capability-table/state.ts)
198
+ - [wiring/capability-table/trust-ledger.ts](../src/wiring/capability-table/trust-ledger.ts)
199
+ - [wiring/capability-table/workflow-apps.ts](../src/wiring/capability-table/workflow-apps.ts)
184
200
 
185
201
  ## Workflow Apps
186
202
 
@@ -251,6 +267,7 @@ multi-agent host -> topology -> blackboard/coordinator
251
267
  - [State Explosion Management](state-explosion-management.7.md)
252
268
  - [STATE-NODE(7)](state-node.7.md)
253
269
  - [Team Collaboration](team-collaboration.7.md)
270
+ - [Trust Audit Anchor](trust-audit-anchor.7.md)
254
271
  - [Trust Model & Limitations](trust-model.md)
255
272
  - [Unix-Inspired Workflow Principles](unix-principles.md)
256
273
  - [Vendor Manifest Loadability](vendor-manifest-loadability.7.md)
@@ -263,6 +280,8 @@ multi-agent host -> topology -> blackboard/coordinator
263
280
 
264
281
  Smoke tests mirror the public contracts. The high-signal suites are:
265
282
 
283
+ - [agent-backend-concurrent-user-env-smoke.js](../test/agent-backend-concurrent-user-env-smoke.js)
284
+ - [agent-backend-user-env-smoke.js](../test/agent-backend-user-env-smoke.js)
266
285
  - [agent-config-atomic-write-smoke.js](../test/agent-config-atomic-write-smoke.js)
267
286
  - [agent-delegation-drive-smoke.js](../test/agent-delegation-drive-smoke.js)
268
287
  - [agent-stream-gate-smoke.js](../test/agent-stream-gate-smoke.js)
@@ -270,6 +289,7 @@ Smoke tests mirror the public contracts. The high-signal suites are:
270
289
  - [architecture-review-fast-automation-smoke.js](../test/architecture-review-fast-automation-smoke.js)
271
290
  - [architecture-review-fast-phase-cache-smoke.js](../test/architecture-review-fast-phase-cache-smoke.js)
272
291
  - [architecture-review-fast-smoke.js](../test/architecture-review-fast-smoke.js)
292
+ - [architecture-review-question-aware-smoke.js](../test/architecture-review-question-aware-smoke.js)
273
293
  - [artifact-integrity-smoke.js](../test/artifact-integrity-smoke.js)
274
294
  - [audit-verify-smoke.js](../test/audit-verify-smoke.js)
275
295
  - [backend-registry-smoke.js](../test/backend-registry-smoke.js)
@@ -310,6 +330,7 @@ Smoke tests mirror the public contracts. The high-signal suites are:
310
330
  - [deferred-checkpoint-batching-smoke.js](../test/deferred-checkpoint-batching-smoke.js)
311
331
  - [demo-bundle-smoke.js](../test/demo-bundle-smoke.js)
312
332
  - [det-ids-b-smoke.js](../test/det-ids-b-smoke.js)
333
+ - [dispatch-legacy-burndown-smoke.js](../test/dispatch-legacy-burndown-smoke.js)
313
334
  - [doctor-smoke.js](../test/doctor-smoke.js)
314
335
  - [dogfood-architecture-review-smoke.js](../test/dogfood-architecture-review-smoke.js)
315
336
  - [dogfood-release-smoke.js](../test/dogfood-release-smoke.js)
@@ -339,6 +360,7 @@ Smoke tests mirror the public contracts. The high-signal suites are:
339
360
  - [mcp-app-surface-smoke.js](../test/mcp-app-surface-smoke.js)
340
361
  - [mcp-surface-registry-smoke.js](../test/mcp-surface-registry-smoke.js)
341
362
  - [mcp-tool-call-coverage-smoke.js](../test/mcp-tool-call-coverage-smoke.js)
363
+ - [metrics-summary-limit-smoke.js](../test/metrics-summary-limit-smoke.js)
342
364
  - [multi-agent-cli-mcp-surface-smoke.js](../test/multi-agent-cli-mcp-surface-smoke.js)
343
365
  - [multi-agent-eval-determinism-regression-smoke.js](../test/multi-agent-eval-determinism-regression-smoke.js)
344
366
  - [multi-agent-eval-replay-harness-smoke.js](../test/multi-agent-eval-replay-harness-smoke.js)
@@ -405,11 +427,13 @@ Smoke tests mirror the public contracts. The high-signal suites are:
405
427
  - [run-restore-failclosed-smoke.js](../test/run-restore-failclosed-smoke.js)
406
428
  - [run-resume-drive-smoke.js](../test/run-resume-drive-smoke.js)
407
429
  - [run-retention-reclamation-smoke.js](../test/run-retention-reclamation-smoke.js)
430
+ - [run-state-lock-concurrency-smoke.js](../test/run-state-lock-concurrency-smoke.js)
408
431
  - [sample-determinism-smoke.js](../test/sample-determinism-smoke.js)
409
432
  - [sandbox-env-batch-hardening-smoke.js](../test/sandbox-env-batch-hardening-smoke.js)
410
433
  - [sandbox-profile-smoke.js](../test/sandbox-profile-smoke.js)
411
434
  - [sched-policy-validation-smoke.js](../test/sched-policy-validation-smoke.js)
412
435
  - [schedule-routine-daemon-smoke.js](../test/schedule-routine-daemon-smoke.js)
436
+ - [scheduling-routine-lock-concurrency-smoke.js](../test/scheduling-routine-lock-concurrency-smoke.js)
413
437
  - [schema-validation-smoke.js](../test/schema-validation-smoke.js)
414
438
  - [security-trust-hardening-smoke.js](../test/security-trust-hardening-smoke.js)
415
439
  - [self-audit-hardening-smoke.js](../test/self-audit-hardening-smoke.js)
@@ -427,6 +451,7 @@ Smoke tests mirror the public contracts. The high-signal suites are:
427
451
  - [telemetry-metrics-coverage-smoke.js](../test/telemetry-metrics-coverage-smoke.js)
428
452
  - [telemetry-verify-signatures-smoke.js](../test/telemetry-verify-signatures-smoke.js)
429
453
  - [token-budget-enforcement-smoke.js](../test/token-budget-enforcement-smoke.js)
454
+ - [trust-audit-anchor-smoke.js](../test/trust-audit-anchor-smoke.js)
430
455
  - [vendor-manifest-load-smoke.js](../test/vendor-manifest-load-smoke.js)
431
456
  - [vendor-preflight-smoke.js](../test/vendor-preflight-smoke.js)
432
457
  - [verifier-gated-commit-smoke.js](../test/verifier-gated-commit-smoke.js)
@@ -191,3 +191,7 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
191
191
  0.1.98
192
192
 
193
193
  0.2.0
194
+
195
+ 0.2.1
196
+
197
+ 0.2.2
@@ -331,3 +331,7 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
331
331
  0.1.98
332
332
 
333
333
  0.2.0
334
+
335
+ 0.2.1
336
+
337
+ 0.2.2
@@ -294,3 +294,7 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
294
294
  0.1.98
295
295
 
296
296
  0.2.0
297
+
298
+ 0.2.1
299
+
300
+ 0.2.2
@@ -1,15 +1,15 @@
1
1
  # Remote-Source Review (`--link`)
2
2
 
3
3
  CW v0.1.91 lets you point a review at **any repository on the internet** instead
4
- of only a local path: `cw -q "what are the risks?" --link <url>`. CW materializes
4
+ of only a local path: `cw -q "how does this work?" --link <url>`. CW materializes
5
5
  the remote into a local checkout and runs the **existing** review pipeline against
6
6
  it — identical downstream to reviewing a folder. A URL passed to `-dir`/`--repo`
7
7
  is auto-detected, so `--link <url>` and `-dir <url>` are equivalent.
8
8
 
9
9
  ```bash
10
- cw -q "What are the risks?" --link https://github.com/owner/repo
11
- cw -q "What are the risks?" --link git@gitlab.com:owner/repo.git --ref v1.2.0
12
- cw -q "What are the risks?" --link https://github.com/owner/repo/archive/refs/heads/main.tar.gz
10
+ cw -q "How does auth work end-to-end here?" --link https://github.com/owner/repo
11
+ cw -q "What are the security risks?" --link git@gitlab.com:owner/repo.git --ref v1.2.0
12
+ cw -q "Is it safe to swap this queue for Redis?" --link https://github.com/owner/repo/archive/refs/heads/main.tar.gz
13
13
  cw -q "..." --link <url> --check # validate the URL + tooling WITHOUT fetching
14
14
  ```
15
15
 
@@ -127,7 +127,7 @@ npx cool-workflow demo bundle
127
127
  # catching both offline with only the embedded public key.
128
128
 
129
129
  # Run the review AND get a shippable, client-verifiable bundle from ONE command:
130
- cw quickstart architecture-review --repo . --question "What are the risks?" \
130
+ cw quickstart architecture-review --repo . --question "How does auth work end-to-end here?" \
131
131
  --agent-command "claude -p" --bundle --with-trust-key ./trust-pub.pem
132
132
  # -> after the drive COMPLETES, the run is sealed into a self-verified bundle and
133
133
  # the verdict is folded into the quickstart JSON (result.bundle). Exits non-zero
@@ -474,3 +474,7 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
474
474
  0.1.98
475
475
 
476
476
  0.2.0
477
+
478
+ 0.2.1
479
+
480
+ 0.2.2
@@ -263,3 +263,7 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
263
263
  0.1.98
264
264
 
265
265
  0.2.0
266
+
267
+ 0.2.1
268
+
269
+ 0.2.2
@@ -37,7 +37,9 @@ Event sources are clear:
37
37
  - `runtime-derived`: CW got the event from run state.
38
38
 
39
39
  CW does not keep secrets or raw environment values. Environment audit records
40
- keep names only.
40
+ keep names only — for example, `worker.agent-env` records which provider-namespace
41
+ env var NAMES (`CW_`, `ANTHROPIC_`, `OPENAI_`, and similar) were forwarded from
42
+ the host process to a delegated agent child, queryable via `cw audit summary`/`cw audit`.
41
43
 
42
44
  ## Enforcement Boundary
43
45
 
@@ -49,6 +51,34 @@ The agent host must still make certain of OS-level read isolation, write isolati
49
51
  limits on process execution, limits on network, and environment filtering.
50
52
  The audit layer lets you look at that boundary; it is not a kernel.
51
53
 
54
+ ## Requiring Attested Telemetry
55
+
56
+ `CW_REQUIRE_ATTESTED_TELEMETRY=1` (or `--require-attested-telemetry`, or a
57
+ durable `agent-config.json` field) is an opt-in, off-by-default gate: once
58
+ on, CW refuses to accept ANY worker result whose usage telemetry is not
59
+ cryptographically `attested` — a delegated hop whose signature does not
60
+ verify, and now also a MANUAL accept (`cw worker output`, `cw result`) that
61
+ carries no delegation telemetry at all. The manual shape used to slip past
62
+ the gate silently; it is now blocked the same way, with its own code
63
+ (`telemetry-missing-blocked`, distinct from a present-but-unverified hop's
64
+ `telemetry-unattested-blocked`).
65
+
66
+ An operator who genuinely needs to accept an unattested manual result under
67
+ the require flag passes `--allow-unattested` (MCP: `allowUnattested`). This
68
+ is never silent: it writes a `telemetry.gate-override` trust-audit event
69
+ (`decision: "allowed"`, `source: "operator"`) into the same hash-chained log
70
+ every other audit decision goes through, so the override itself is part of
71
+ the auditable record, not a hole in it.
72
+
73
+ A result-cache hit (the drive loop replaying a previously accepted result) is
74
+ never re-blocked by this gate — the underlying result was already gated (or
75
+ overridden) at its first acceptance. When the require flag is on, a cache
76
+ accept still records a `telemetry.cache-accept` event, so the audit trail
77
+ shows which accepts came from a fresh hop and which from the cache.
78
+
79
+ With the require flag off (the default), none of this changes: a plain
80
+ `cw worker output` / `cw result` behaves exactly as before.
81
+
52
82
  ## CLI
53
83
 
54
84
  ```bash
@@ -65,6 +95,8 @@ node scripts/cw.js audit decision <run-id> <worker-id> --path <path>
65
95
  node scripts/cw.js audit decision <run-id> <worker-id> --command "npm test"
66
96
  node scripts/cw.js audit decision <run-id> <worker-id> --network example.com
67
97
  node scripts/cw.js audit decision <run-id> <worker-id> --env SECRET_NAME
98
+ node scripts/cw.js worker output <run-id> <worker-id> <result-file> [--allow-unattested]
99
+ node scripts/cw.js result <run-id> <task-id> <result-file> [--allow-unattested]
68
100
  ```
69
101
 
70
102
  Denied audit decisions are put into audit files and joined to feedback/error
@@ -320,3 +320,7 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
320
320
  0.1.98
321
321
 
322
322
  0.2.0
323
+
324
+ 0.2.1
325
+
326
+ 0.2.2
@@ -256,3 +256,7 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
256
256
  0.1.98
257
257
 
258
258
  0.2.0
259
+
260
+ 0.2.1
261
+
262
+ 0.2.2
@@ -0,0 +1,69 @@
1
+ # Trust Audit Anchor
2
+
3
+ CW v0.2.1 adds the Trust Audit Anchor: a way to see when the END of a run's
4
+ trust-audit log was cut off. The hash chain in `audit/events.jsonl` lets
5
+ `cw audit verify` see an edited event, a removed middle event, a bad line, and
6
+ a mixed-era forgery. But one tamper shape gets past a pure chain walk: take
7
+ the last N lines off the file, and what is left is a shorter but fully
8
+ consistent chain — verify stays green. The anchor closes that hole, and it
9
+ does so without changing any old output byte.
10
+
11
+ ## Design Discipline
12
+
13
+ - Mechanism, not policy: the kernel gives you two small parts — a read of the
14
+ chain head, and a check against a head you saved earlier. WHEN you save a
15
+ head (after a run, before you publish, at export time) is your policy.
16
+ - Fail-closed: a saved head that is not on the chain, or an event count that
17
+ comes up short, makes verify exit non-zero with the distinct check code
18
+ `trust-audit-truncated`. A bad `--expect-head` / `--expect-count` value is
19
+ an error, never a check silently made weaker.
20
+ - POLA: with no anchor flags, `cw audit verify` output is byte-for-byte what
21
+ it was before this feature (no `anchor` key, same checks, same exit rules).
22
+ - Reuse: the anchor rides on the existing eventHash chain — no new file, no
23
+ new state, no new hash form. `cw audit head` is a read-only projection.
24
+ - Parity: `audit.head` is on both front doors (`cw audit head` and the MCP
25
+ tool `cw_audit_head`); the anchor args are on both `cw audit verify` and
26
+ `cw_audit_verify` (`expectHead` / `expectCount`).
27
+
28
+ ## CLI
29
+
30
+ ```text
31
+ node dist/cli.js audit head <run-id>
32
+ # -> { "schemaVersion": 1, "runId": "...", "eventCount": 87,
33
+ # "headHash": "sha256:..." }
34
+
35
+ node dist/cli.js audit verify <run-id> --expect-head <hash> --expect-count <n>
36
+ # green: verified true, "anchor": { ..., "satisfied": true }, exit 0
37
+ # cut tail: verified false, checks carry trust-audit-truncated, exit 1
38
+ ```
39
+
40
+ The head is the hash the NEXT appended event will link from: the last event's
41
+ `eventHash`, or the run's genesis hash when the log is empty. Save the pair
42
+ `{headHash, eventCount}` somewhere the log's writer cannot reach — a CI
43
+ variable, a note in your PR, the output of `cw run export` (the export
44
+ manifest hashes the event log bytes, so an export IS an anchor in file form).
45
+
46
+ ## How the check works
47
+
48
+ `verifyTrustAudit` walks the chain as before, and keeps the trail of head
49
+ hashes it saw (genesis, then the hash after each event). With an anchor:
50
+
51
+ - `expectCount`: the walked log must have at least that many events. Fewer =
52
+ `trust-audit-truncated` (check name `anchor-count`).
53
+ - `expectHead`: the saved head must be ON the trail. A log that was cut and
54
+ then padded back with new events reaches the old count, but the new events
55
+ link from an earlier point — the old head is no longer on the trail, so
56
+ this still fails (check name `anchor-head`).
57
+
58
+ ## Compatibility
59
+
60
+ Trust Audit Anchor is introduced in CW v0.2.1. Fields are additive and
61
+ optional; older run state loads unchanged. A plain `cw audit verify` keeps
62
+ its exact old output. The `anchor` key appears in the JSON only when the
63
+ caller passed an anchor flag.
64
+
65
+ ## See Also
66
+
67
+ security-trust-hardening(7), cli-mcp-parity(7), report-verifiable-bundle(7)
68
+
69
+ 0.2.2
@@ -96,11 +96,21 @@ The console is read-only. It offers no actions. If a later release adds an actio
96
96
  capability core entry that already exists — never a side code path — so it cannot drift from the
97
97
  CLI/MCP and is covered by the parity gate.
98
98
 
99
+ Authentication is opt-in, not on by default: set `CW_WORKBENCH_TOKEN` and every
100
+ request must carry it as `Authorization: Bearer <token>` or `?token=<token>`
101
+ (timing-safe compare); left unset, the loopback/read-only/GET-only controls
102
+ above are the only defense and any local process can read. Pass
103
+ `--require-token` to `cw workbench serve` to fail closed instead: the server
104
+ refuses to start at all unless `CW_WORKBENCH_TOKEN` is already set. This is a
105
+ strict opt-in — leaving it off keeps today's default behavior unchanged.
106
+ (`--require-token` has no effect on `cw_workbench_serve`'s MCP path, which
107
+ never actually binds a listener.)
108
+
99
109
  ## Surfaces
100
110
 
101
111
  ```
102
112
  cw workbench view <run-id> [--json] # five-panel WorkbenchRunView for one run
103
- cw workbench serve [--port N] [--scope repo|home] [--once|--json]
113
+ cw workbench serve [--port N] [--scope repo|home] [--once|--json] [--require-token]
104
114
  ```
105
115
 
106
116
  `cw workbench serve` with `--once`/`--json` prints the serve descriptor (bind
@@ -264,3 +274,7 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
264
274
  0.1.98
265
275
 
266
276
  0.2.0
277
+
278
+ 0.2.1
279
+
280
+ 0.2.2
@@ -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.2.0",
5
+ "version": "0.2.2",
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.2.0",
3
+ "version": "0.2.2",
4
4
  "bin": {
5
5
  "cool-workflow": "scripts/cw.js",
6
6
  "cw": "scripts/cw.js"
@@ -43,6 +43,7 @@
43
43
  "build": "tsc -p tsconfig.json",
44
44
  "check": "tsc -p tsconfig.json --noEmit",
45
45
  "dist:check": "node scripts/dist-drift-check.js",
46
+ "purity:check": "node scripts/purity-gate.js",
46
47
  "golden-path": "node scripts/golden-path.js",
47
48
  "dogfood:release": "node scripts/dogfood-release.js",
48
49
  "canonical-apps": "node scripts/canonical-apps.js",
@@ -68,6 +69,7 @@
68
69
  "test:fast": "npm run build --if-present && node dist/cli.js version > /dev/null && node test/run-all.js --concurrency auto",
69
70
  "test:ci": "npm run build && node dist/cli.js version > /dev/null && node test/run-all.js --sample 55",
70
71
  "test:coverage": "node dist/cli.js version > /dev/null && node scripts/coverage-gate.js --concurrency auto",
72
+ "test:unit": "node dist/cli.js version > /dev/null && node test/run-unit.js",
71
73
  "eval:replay": "tsc -p tsconfig.json && node test/multi-agent-eval-replay-harness-smoke.js",
72
74
  "ci": "npm run build && npm run check && npm run test && npm run release:check",
73
75
  "validate:schema": "node scripts/validate-run-state-schema.js"
@@ -18,7 +18,7 @@ cw:result block that EXACTLY follows this schema:
18
18
  "findings": [
19
19
  {
20
20
  "id": "unique-kebab-id",
21
- "title": "short risk title",
21
+ "title": "short finding title",
22
22
  "severity": "P0",
23
23
  "classification": "real",
24
24
  "evidence": ["path/to/file.ts:42"]
@@ -471,6 +471,25 @@ function persistStderr(resultPath, text) {
471
471
  }
472
472
  }
473
473
 
474
+ // Build the failure-path diagnostic string for a non-zero exit. Real OS-level
475
+ // stderr wins when present (the common case: a crash, a killed process, a
476
+ // vendor CLI that DOES write to stderr) — byte-identical to before this
477
+ // helper existed. When stderr is EMPTY, fall back to whatever partial/
478
+ // structured text the wrapper already parsed from stdout before the child
479
+ // died (a stream-json `result` event with is_error:true is how claude
480
+ // reports terminal errors like an auth failure — that text was being parsed
481
+ // and then silently thrown away). If there is no partial text either, say so
482
+ // plainly instead of just repeating the bare exit code. Never throws; always
483
+ // returns a non-empty string.
484
+ function buildFailureDetail({ label, code, childStderr, partialText }) {
485
+ const stderrText = String(childStderr || "").trim();
486
+ if (stderrText) return stderrText;
487
+ const partial = String(partialText || "").trim();
488
+ const codeText = `${label} exited ${code === null ? "(timeout/killed)" : code}`;
489
+ if (partial) return `${codeText} — ${label} stdout (no stderr) said: ${partial}`;
490
+ return codeText;
491
+ }
492
+
474
493
  module.exports = {
475
494
  RESULT_CONTRACT,
476
495
  buildPrompt,
@@ -485,5 +504,6 @@ module.exports = {
485
504
  flushJsonLines,
486
505
  writeResult,
487
506
  emitReport,
488
- persistStderr // save a failed agent's stderr to <workerDir>/logs/agent-stderr.log (shared by all wrappers)
507
+ persistStderr, // save a failed agent's stderr to <workerDir>/logs/agent-stderr.log (shared by all wrappers)
508
+ buildFailureDetail // fold in parsed partial stdout text when real stderr is empty (shared by all wrappers)
489
509
  };
@@ -34,7 +34,7 @@ const { spawn, spawnSync } = require("node:child_process");
34
34
  // wrappers instead of carrying a private copy. A drifted inline copy (ASCII
35
35
  // hyphens silently became em-dashes here) meant claude was sent a different
36
36
  // instruction text than the other providers for the same contract.
37
- const { buildPrompt, createRenderer, persistStderr, toolLabel, summarizeToolResult } = require("./agent-adapter-core");
37
+ const { buildPrompt, createRenderer, persistStderr, toolLabel, summarizeToolResult, buildFailureDetail } = require("./agent-adapter-core");
38
38
 
39
39
  const inputPath = process.argv[2];
40
40
  const resultPath = process.argv[3];
@@ -179,9 +179,14 @@ child.on("close", (code) => {
179
179
  render.finishLive(); // stop the spinner + restore the cursor BEFORE any further output
180
180
  render.writeTranscript(transcriptPath); // full narration + tool I/O always saved
181
181
  if (code !== 0) {
182
- const detail = childStderr.trim() || `claude exited ${code === null ? "(timeout/killed)" : code}`;
182
+ const stderrText = childStderr.trim();
183
+ // childStderr is the OS-level stream; claude's real failure reason (auth,
184
+ // rate limit, relay error) is often reported as a stream-json `result`
185
+ // event on STDOUT instead, already parsed into `resultText` above — fold
186
+ // it in here so a silent-stderr failure still leaves a readable reason.
187
+ const detail = buildFailureDetail({ label: "claude", code, childStderr: stderrText, partialText: resultText });
183
188
  persistStderr(resultPath, detail);
184
- if (childStderr.trim()) process.stderr.write(`${childStderr.trim()}\n`);
189
+ if (stderrText) process.stderr.write(`${stderrText}\n`);
185
190
  process.stderr.write(`claude exited ${code === null ? "(timeout/killed)" : code}\n`);
186
191
  process.exit(code === null ? 1 : code);
187
192
  }
@@ -17,6 +17,7 @@
17
17
  const path = require("node:path");
18
18
  const { spawn } = require("node:child_process");
19
19
  const {
20
+ buildFailureDetail,
20
21
  buildPrompt,
21
22
  createRenderer,
22
23
  emitReport,
@@ -94,7 +95,10 @@ child.on("close", (code) => {
94
95
  render.finishLive();
95
96
  render.writeTranscript(transcriptPath);
96
97
  if (code !== 0) {
97
- const detail = childStderr.trim() || `gemini exited ${code === null ? "(timeout/killed)" : code}`;
98
+ // gemini's real failure reason is often only in the NDJSON text/result
99
+ // fragments already parsed into `state`, not in raw OS-level stderr.
100
+ const partial = state.finalResult || state.textFragments.join("\n\n");
101
+ const detail = buildFailureDetail({ label: "gemini", code, childStderr: childStderr.trim(), partialText: partial });
98
102
  persistStderr(resultPath, detail);
99
103
  process.stderr.write(`${detail}\n`);
100
104
  process.exit(code === null ? 1 : code);
@@ -22,6 +22,7 @@
22
22
  const path = require("node:path");
23
23
  const { spawn } = require("node:child_process");
24
24
  const {
25
+ buildFailureDetail,
25
26
  buildPrompt,
26
27
  createRenderer,
27
28
  emitReport,
@@ -147,7 +148,10 @@ child.on("close", (code) => {
147
148
  render.finishLive();
148
149
  render.writeTranscript(transcriptPath);
149
150
  if (code !== 0) {
150
- const detail = childStderr.trim() || `opencode exited ${code === null ? "(timeout/killed)" : code}`;
151
+ // opencode's real failure reason is often only in the JSONL text/result
152
+ // fragments already parsed into `state`, not in raw OS-level stderr.
153
+ const partial = state.finalResult || state.lastMessageText || state.textFragments.join("\n\n");
154
+ const detail = buildFailureDetail({ label: "opencode", code, childStderr: childStderr.trim(), partialText: partial });
151
155
  persistStderr(resultPath, detail);
152
156
  process.stderr.write(`${detail}\n`);
153
157
  process.exit(code === null ? 1 : code);
@@ -46,6 +46,23 @@ function replaceFirstVersionField(absPath, next) {
46
46
  return true;
47
47
  }
48
48
 
49
+ function replaceLockfileVersions(absPath, next) {
50
+ // Move BOTH lockfile version fields with targeted string swaps, so the byte
51
+ // formatting npm wrote is kept as it is: the top-level "version" (the first
52
+ // one in the file) and the root-package entry `"": { "name": ..., "version": ... }`.
53
+ // Dependency entries are not touched — the second swap is keyed on the
54
+ // `"": {` root-package opening, which is present only once.
55
+ const text = fs.readFileSync(absPath, "utf8");
56
+ let updated = text.replace(/"version":\s*"[^"]*"/, `"version": "${next}"`);
57
+ updated = updated.replace(
58
+ /("":\s*\{\s*"name":\s*"[^"]*",\s*"version":\s*)"[^"]*"/,
59
+ `$1"${next}"`
60
+ );
61
+ if (updated === text) return false;
62
+ fs.writeFileSync(absPath, updated);
63
+ return true;
64
+ }
65
+
49
66
  function setNestedVersion(absPath, next) {
50
67
  // For files where the first `"version"` is NOT the right one, parse + set.
51
68
  const json = JSON.parse(fs.readFileSync(absPath, "utf8"));
@@ -75,9 +92,13 @@ function main() {
75
92
  // 1. package.json (the single source of truth version:sync now reads from)
76
93
  if (replaceFirstVersionField(path.join(pluginRoot, "package.json"), next)) note("package.json");
77
94
 
78
- // 2. package-lock.json (gitignored install artifact; only if present)
95
+ // 2. package-lock.json (tracked; only if present). The lockfile keeps the
96
+ // version in TWO places: the top-level "version" and the root-package
97
+ // entry packages[""].version. The old code moved only the first one, so
98
+ // the root-package entry kept an old version till the next `npm install`
99
+ // (the v0.1.97 drift seen after the v0.2.0/v0.2.1 cuts). Move both here.
79
100
  const lock = path.join(pluginRoot, "package-lock.json");
80
- if (fs.existsSync(lock) && replaceFirstVersionField(lock, next)) note("package-lock.json");
101
+ if (fs.existsSync(lock) && replaceLockfileVersions(lock, next)) note("package-lock.json");
81
102
 
82
103
  // 2b. Official MCP Registry server metadata (top-level server version + npm package version).
83
104
  const serverJson = path.join(pluginRoot, "server.json");
@@ -218,6 +239,7 @@ function contentSurfaceFiles(next) {
218
239
  { path: "plugins/cool-workflow/docs/run-retention-reclamation.7.md", needle: next, desc: "run retention doc" },
219
240
  { path: "plugins/cool-workflow/docs/durable-state-and-locking.7.md", needle: next, desc: "durable state doc" },
220
241
  { path: "plugins/cool-workflow/docs/release-and-migration.7.md", needle: next, desc: "release & migration doc" },
242
+ { path: "plugins/cool-workflow/docs/trust-audit-anchor.7.md", needle: next, desc: "trust audit anchor doc" },
221
243
  ];
222
244
  }
223
245
 
@@ -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.2.0",
86
+ "Cool Workflow v0.2.2",
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.2.0");
120
+ assert.equal(summary.version, "0.2.2");
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.2.0");
127
+ assert.equal(shown.app.version, "0.2.2");
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.2.0");
138
+ assert.equal(state.workflow.app.version, "0.2.2");
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`);
@@ -6,7 +6,7 @@ const fs = require("node:fs");
6
6
  const path = require("node:path");
7
7
  const { CoolWorkflowRunner } = require("../dist/shell/orchestrator.js");
8
8
 
9
- const TARGET_VERSION = "0.2.0";
9
+ const TARGET_VERSION = "0.2.2";
10
10
  const PREVIOUS_VERSION = "0.1.31";
11
11
  const pluginRoot = path.resolve(__dirname, "..");
12
12
  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.2.0");
36
+ assert.equal(appValidation.summary.version, "0.2.2");
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.2.0 end-to-end golden path."
45
+ "Prove the deterministic v0.2.2 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.2.0");
55
+ assert.equal(state.workflow.app.version, "0.2.2");
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\.2\.0/);
198
+ assert.match(report, /Workflow App: end-to-end-golden-path@0\.2\.2/);
199
199
  assert.match(report, /## Candidates/);
200
200
  assert.match(report, /## Trust Audit/);
201
201
  assert.match(report, /## Acceptance Rationale/);