cool-workflow 0.1.95 → 0.1.96

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 (67) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/apps/architecture-review/app.json +1 -1
  4. package/apps/architecture-review/workflow.js +3 -3
  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/agent-config.js +2 -1
  11. package/dist/dispatch.js +12 -6
  12. package/dist/evidence-grounding.js +18 -13
  13. package/dist/execution-backend/probes.js +22 -6
  14. package/dist/execution-backend.js +37 -4
  15. package/dist/node-snapshot.js +3 -3
  16. package/dist/orchestrator/lifecycle-operations.js +13 -5
  17. package/dist/orchestrator.js +26 -1
  18. package/dist/reclamation.js +8 -2
  19. package/dist/run-registry/derive.js +4 -1
  20. package/dist/scheduler.js +14 -14
  21. package/dist/schema-validate.js +8 -2
  22. package/dist/state-explosion/helpers.js +4 -21
  23. package/dist/state-explosion/size.js +63 -0
  24. package/dist/state-explosion.js +18 -71
  25. package/dist/state.js +47 -9
  26. package/dist/trust-audit.js +27 -2
  27. package/dist/util/fingerprint.js +19 -0
  28. package/dist/util/fingerprint.test.js +27 -0
  29. package/dist/version.js +1 -1
  30. package/dist/workbench-host.js +11 -0
  31. package/dist/workbench.js +19 -17
  32. package/dist/worker-isolation.js +25 -1
  33. package/docs/agent-delegation-drive.7.md +64 -1
  34. package/docs/cli-mcp-parity.7.md +2 -0
  35. package/docs/contract-migration-tooling.7.md +2 -0
  36. package/docs/control-plane-scheduling.7.md +2 -0
  37. package/docs/durable-state-and-locking.7.md +2 -0
  38. package/docs/evidence-adoption-reasoning-chain.7.md +2 -0
  39. package/docs/execution-backends.7.md +2 -0
  40. package/docs/multi-agent-cli-mcp-surface.7.md +2 -0
  41. package/docs/multi-agent-eval-replay-harness.7.md +2 -0
  42. package/docs/multi-agent-operator-ux.7.md +2 -0
  43. package/docs/node-snapshot-diff-replay.7.md +2 -0
  44. package/docs/observability-cost-accounting.7.md +2 -0
  45. package/docs/project-index.md +7 -3
  46. package/docs/real-execution-backends.7.md +2 -0
  47. package/docs/release-and-migration.7.md +2 -0
  48. package/docs/release-tooling.7.md +18 -0
  49. package/docs/routines.md +30 -0
  50. package/docs/run-registry-control-plane.7.md +2 -0
  51. package/docs/run-retention-reclamation.7.md +2 -0
  52. package/docs/sandbox-profiles.7.md +15 -0
  53. package/docs/state-explosion-management.7.md +2 -0
  54. package/docs/team-collaboration.7.md +2 -0
  55. package/docs/web-desktop-workbench.7.md +2 -0
  56. package/manifest/plugin.manifest.json +1 -1
  57. package/package.json +5 -3
  58. package/scripts/agents/agent-adapter-core.js +22 -1
  59. package/scripts/agents/claude-p-agent.js +10 -2
  60. package/scripts/agents/codex-agent.js +22 -2
  61. package/scripts/agents/gemini-agent.js +10 -2
  62. package/scripts/agents/opencode-agent.js +10 -2
  63. package/scripts/canonical-apps.js +4 -4
  64. package/scripts/dogfood-release.js +1 -1
  65. package/scripts/golden-path.js +4 -4
  66. package/scripts/release-flow.js +10 -0
  67. package/scripts/release-gate.sh +1 -1
@@ -549,3 +549,5 @@ CLI golden-path fixes: `cw -q "…"` routes the question (was read as an app id
549
549
  0.1.94
550
550
 
551
551
  0.1.95
552
+
553
+ 0.1.96
@@ -167,3 +167,5 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
167
167
  0.1.94
168
168
 
169
169
  0.1.95
170
+
171
+ 0.1.96
@@ -151,3 +151,5 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
151
151
  0.1.94
152
152
 
153
153
  0.1.95
154
+
155
+ 0.1.96
@@ -150,3 +150,5 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
150
150
  0.1.94
151
151
 
152
152
  0.1.95
153
+
154
+ 0.1.96
@@ -311,3 +311,5 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
311
311
  0.1.94
312
312
 
313
313
  0.1.95
314
+
315
+ 0.1.96
@@ -341,3 +341,5 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
341
341
  0.1.94
342
342
 
343
343
  0.1.95
344
+
345
+ 0.1.96
@@ -317,3 +317,5 @@ The host-facing surface tracks the CLI golden-path fixes (`cw -q` routing + repo
317
317
  0.1.94
318
318
 
319
319
  0.1.95
320
+
321
+ 0.1.96
@@ -343,3 +343,5 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
343
343
  0.1.94
344
344
 
345
345
  0.1.95
346
+
347
+ 0.1.96
@@ -355,3 +355,5 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
355
355
  0.1.94
356
356
 
357
357
  0.1.95
358
+
359
+ 0.1.96
@@ -176,3 +176,5 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
176
176
  0.1.94
177
177
 
178
178
  0.1.95
179
+
180
+ 0.1.96
@@ -235,3 +235,5 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
235
235
  0.1.94
236
236
 
237
237
  0.1.95
238
+
239
+ 0.1.96
@@ -1,15 +1,15 @@
1
1
  # Cool Workflow Project Index
2
2
 
3
- Generated from the current repository code on 2026-06-27 by `npm run sync:project-index`.
3
+ Generated from the current repository code on 2026-06-28 by `npm run sync:project-index`.
4
4
 
5
5
  ## Snapshot
6
6
 
7
7
  - Package: `cool-workflow`
8
- - Version: `0.1.95`
8
+ - Version: `0.1.96`
9
9
  - Source modules: `68`
10
10
  - Workflow apps: `8`
11
11
  - Docs: `53`
12
- - Smoke tests: `154`
12
+ - Smoke tests: `158`
13
13
  - Repository: https://github.com/coo1white/cool-workflow
14
14
 
15
15
  ## Architecture
@@ -194,6 +194,7 @@ multi-agent host -> topology -> blackboard/coordinator
194
194
 
195
195
  Smoke tests mirror the public contracts. The high-signal suites are:
196
196
 
197
+ - [agent-config-atomic-write-smoke.js](../test/agent-config-atomic-write-smoke.js)
197
198
  - [agent-delegation-drive-smoke.js](../test/agent-delegation-drive-smoke.js)
198
199
  - [append-run-node-no-realloc-smoke.js](../test/append-run-node-no-realloc-smoke.js)
199
200
  - [architecture-review-fast-automation-smoke.js](../test/architecture-review-fast-automation-smoke.js)
@@ -224,6 +225,7 @@ Smoke tests mirror the public contracts. The high-signal suites are:
224
225
  - [cli-render-smoke.js](../test/cli-render-smoke.js)
225
226
  - [clones-gc-smoke.js](../test/clones-gc-smoke.js)
226
227
  - [codex-agent-wrapper-smoke.js](../test/codex-agent-wrapper-smoke.js)
228
+ - [collaboration-ops-unit-smoke.js](../test/collaboration-ops-unit-smoke.js)
227
229
  - [concurrency-default-smoke.js](../test/concurrency-default-smoke.js)
228
230
  - [concurrent-failure-semantics-smoke.js](../test/concurrent-failure-semantics-smoke.js)
229
231
  - [concurrent-workflow-dsl-smoke.js](../test/concurrent-workflow-dsl-smoke.js)
@@ -248,6 +250,7 @@ Smoke tests mirror the public contracts. The high-signal suites are:
248
250
  - [execution-backend-agent-smoke.js](../test/execution-backend-agent-smoke.js)
249
251
  - [execution-backend-ci-smoke.js](../test/execution-backend-ci-smoke.js)
250
252
  - [execution-backends-smoke.js](../test/execution-backends-smoke.js)
253
+ - [feedback-ops-unit-smoke.js](../test/feedback-ops-unit-smoke.js)
251
254
  - [freebsd-audit-fixes-smoke.js](../test/freebsd-audit-fixes-smoke.js)
252
255
  - [gemini-agent-wrapper-smoke.js](../test/gemini-agent-wrapper-smoke.js)
253
256
  - [gemini-opencode-agent-wrapper-smoke.js](../test/gemini-opencode-agent-wrapper-smoke.js)
@@ -257,6 +260,7 @@ Smoke tests mirror the public contracts. The high-signal suites are:
257
260
  - [loop-bounded-expansion-smoke.js](../test/loop-bounded-expansion-smoke.js)
258
261
  - [mcp-app-surface-smoke.js](../test/mcp-app-surface-smoke.js)
259
262
  - [mcp-surface-registry-smoke.js](../test/mcp-surface-registry-smoke.js)
263
+ - [mcp-tool-call-coverage-smoke.js](../test/mcp-tool-call-coverage-smoke.js)
260
264
  - [multi-agent-cli-mcp-surface-smoke.js](../test/multi-agent-cli-mcp-surface-smoke.js)
261
265
  - [multi-agent-eval-determinism-regression-smoke.js](../test/multi-agent-eval-determinism-regression-smoke.js)
262
266
  - [multi-agent-eval-replay-harness-smoke.js](../test/multi-agent-eval-replay-harness-smoke.js)
@@ -183,3 +183,5 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
183
183
  0.1.94
184
184
 
185
185
  0.1.95
186
+
187
+ 0.1.96
@@ -323,3 +323,5 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
323
323
  0.1.94
324
324
 
325
325
  0.1.95
326
+
327
+ 0.1.96
@@ -85,6 +85,22 @@ The dogfood release smoke and the architecture-review dogfood smoke are separate
85
85
  test files. The split keeps the same release and agent-drive proof, but lets
86
86
  `test:ci` schedule the two long checks in parallel.
87
87
 
88
+ ## PR CI Merge Notes
89
+
90
+ When a PR is ready, list open PRs by creation time and merge the oldest ready
91
+ one first. After each merge, check the next PR again; a new main commit may make
92
+ it need a rebase.
93
+
94
+ If main moved after the PR branch was made, replay the PR commits on top of the
95
+ new main in a clean worktree. Keep local unrelated changes out of the rebase and
96
+ out of the PR.
97
+
98
+ Treat CI as the source of truth for what blocks the merge. Read the failed step
99
+ first, fix only that drift, then push again. A common drift is the npm README:
100
+ when `readme-sync-smoke.js` says the package README is stale, run
101
+ `npm run sync:readme`, add only `plugins/cool-workflow/README.md`, and let CI run
102
+ again.
103
+
88
104
  ## Boundary
89
105
 
90
106
  Release Tooling touches only the build/release surfaces. It adds no runtime
@@ -270,3 +286,5 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
270
286
  0.1.94
271
287
 
272
288
  0.1.95
289
+
290
+ 0.1.96
package/docs/routines.md CHANGED
@@ -64,6 +64,36 @@ report path and digest.
64
64
  The `--metrics` flag is not required and it gives back foreground time used plus
65
65
  agent-spawn and result-cache-hit counts for the fast run.
66
66
 
67
+ ## PDCA Blackboard Development Lessons
68
+
69
+ When a task asks for agents to work together, first try the parts CW already
70
+ has:
71
+
72
+ - workflow apps give the work shape
73
+ - worker output gives checked facts
74
+ - the blackboard gives shared state
75
+ - MCP gives tool access to the same state
76
+ - smoke tests prove the loop
77
+
78
+ Do not make a new MCP server when the existing server can show the same run
79
+ state. Add a workflow app first, then prove the app with one smoke that uses
80
+ both CLI and MCP.
81
+
82
+ For a three-agent loop, keep the order plain:
83
+
84
+ ```text
85
+ plan -> build -> audit -> next action
86
+ ```
87
+
88
+ Each agent should write one blackboard message and, when there is a result
89
+ file, one artifact ref. Take a snapshot after the audit and after the next
90
+ action. If audit evidence is missing, let the worker evidence gate refuse the
91
+ result instead of adding a new policy layer.
92
+
93
+ Before a PR, base the branch on `origin/main`, run the generated-doc checks,
94
+ and sync generated docs when the gate says they are stale. This keeps unrelated
95
+ local commits and generated README/index drift out of the work.
96
+
67
97
  ## Boundary
68
98
 
69
99
  CW v0.1.1 does not give managed cloud infrastructure. It gives a local
@@ -466,3 +466,5 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
466
466
  0.1.94
467
467
 
468
468
  0.1.95
469
+
470
+ 0.1.96
@@ -234,3 +234,5 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
234
234
  0.1.94
235
235
 
236
236
  0.1.95
237
+
238
+ 0.1.96
@@ -26,6 +26,21 @@ normalization, worker result acceptance, and durable feedback for denied worker
26
26
  output. The agent host has to do OS-level file access, process execution,
27
27
  network access, and environment filtering.
28
28
 
29
+ **IMPORTANT**: Under the default `node` backend, a sandbox profile's
30
+ `execute`, `network`, and `env` policy is **attested, not enforced**. CW
31
+ validates the policy, records it in the worker manifest, and attests that
32
+ these limits were declared — but the actual enforcement of command execution
33
+ restrictions, network isolation, and environment variable filtering is
34
+ DELEGATED to the host runtime. For full enforcement, use the `container`
35
+ backend (`--backend container`) with Docker/Podman, or apply OS-level
36
+ sandboxing to the agent process. Without OS enforcement, a worker under a
37
+ `locked-down` profile can still run arbitrary commands and access the network.
38
+
39
+ CW also now (v0.1.95) applies `buildChildEnv(policy)` as a baseline for agent
40
+ spawns — only `PATH`, `HOME`, explicit `expose` entries, and well-known
41
+ `CW_*` + LLM provider API key environment variables pass through. The
42
+ operator's other process environment is not inherited by default.
43
+
29
44
  The design goal is simple:
30
45
 
31
46
  ```text
@@ -312,3 +312,5 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
312
312
  0.1.94
313
313
 
314
314
  0.1.95
315
+
316
+ 0.1.96
@@ -248,3 +248,5 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
248
248
  0.1.94
249
249
 
250
250
  0.1.95
251
+
252
+ 0.1.96
@@ -256,3 +256,5 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
256
256
  0.1.94
257
257
 
258
258
  0.1.95
259
+
260
+ 0.1.96
@@ -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.95",
5
+ "version": "0.1.96",
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.95",
3
+ "version": "0.1.96",
4
4
  "bin": {
5
5
  "cool-workflow": "scripts/cw.js",
6
6
  "cw": "scripts/cw.js"
@@ -62,9 +62,11 @@
62
62
  "onramp:check": "node scripts/onramp-check.js --check",
63
63
  "version:sync": "node scripts/version-sync-check.js",
64
64
  "release:check": "node scripts/release-check.js",
65
- "test": "node dist/cli.js version > /dev/null && node test/run-all.js",
65
+ "test": "node dist/cli.js version > /dev/null && node test/run-all.js --fast --sample 35",
66
+ "test:full": "node dist/cli.js version > /dev/null && node test/run-all.js --sample 55",
67
+ "test:gate": "node dist/cli.js version > /dev/null && node test/run-all.js",
66
68
  "test:fast": "npm run build --if-present && node dist/cli.js version > /dev/null && node test/run-all.js --concurrency auto",
67
- "test:ci": "node dist/cli.js version > /dev/null && node test/run-all.js --concurrency auto",
69
+ "test:ci": "npm run build && node dist/cli.js version > /dev/null && node test/run-all.js --sample 55",
68
70
  "test:coverage": "node dist/cli.js version > /dev/null && node scripts/coverage-gate.js --concurrency auto",
69
71
  "eval:replay": "tsc -p tsconfig.json && node test/multi-agent-eval-replay-harness-smoke.js",
70
72
  "ci": "npm run build && npm run check && npm run test && npm run release:check",
@@ -2,6 +2,7 @@
2
2
  "use strict";
3
3
 
4
4
  const fs = require("node:fs");
5
+ const path = require("node:path");
5
6
 
6
7
  const RESULT_CONTRACT = `
7
8
  === HOW TO RETURN YOUR ANSWER (overrides any 'write to result.md' instruction above) ===
@@ -448,6 +449,25 @@ function emitReport(model, usage, resultText) {
448
449
  process.stdout.write(JSON.stringify({ model, usage, result: resultText }));
449
450
  }
450
451
 
452
+ // Drop the failed agent's stderr beside the worker's result.md so a `failed (exit
453
+ // 1)` is readable AFTER the fact. CW core keeps only the child's stdout + exit
454
+ // code (byte-stable evidence), so without this the real reason — a relay 5xx, an
455
+ // auth error, a killed run — is lost. The worker dir already has a `logs/` folder
456
+ // (src/worker-isolation.ts); resultPath is `<workerDir>/result.md`, so its
457
+ // `logs/agent-stderr.log` sibling is the natural home. Advisory only: never throws,
458
+ // never changes the exit code or the recorded evidence.
459
+ function persistStderr(resultPath, text) {
460
+ const t = String(text || "").trim();
461
+ if (!t || !resultPath) return;
462
+ try {
463
+ const dir = path.join(path.dirname(resultPath), "logs");
464
+ fs.mkdirSync(dir, { recursive: true });
465
+ fs.writeFileSync(path.join(dir, "agent-stderr.log"), `${t}\n`, "utf8");
466
+ } catch {
467
+ /* advisory only — diagnostics must never break the run */
468
+ }
469
+ }
470
+
451
471
  module.exports = {
452
472
  RESULT_CONTRACT,
453
473
  buildPrompt,
@@ -461,5 +481,6 @@ module.exports = {
461
481
  parseJsonLines,
462
482
  flushJsonLines,
463
483
  writeResult,
464
- emitReport
484
+ emitReport,
485
+ persistStderr // save a failed agent's stderr to <workerDir>/logs/agent-stderr.log (shared by all wrappers)
465
486
  };
@@ -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, toolLabel, summarizeToolResult } = require("./agent-adapter-core");
37
+ const { buildPrompt, createRenderer, persistStderr, toolLabel, summarizeToolResult } = require("./agent-adapter-core");
38
38
 
39
39
  const inputPath = process.argv[2];
40
40
  const resultPath = process.argv[3];
@@ -56,11 +56,14 @@ if (!streamEnabled) {
56
56
  shell: false
57
57
  });
58
58
  if (child.error) {
59
+ persistStderr(resultPath, `claude spawn failed: ${child.error.message}`);
59
60
  process.stderr.write(`claude spawn failed: ${child.error.message}\n`);
60
61
  process.exit(1);
61
62
  }
62
63
  if (child.status !== 0) {
63
- process.stderr.write(String(child.stderr || `claude exited ${child.status}`));
64
+ const detail = String(child.stderr || `claude exited ${child.status}`);
65
+ persistStderr(resultPath, detail);
66
+ process.stderr.write(detail);
64
67
  process.exit(child.status === null ? 1 : child.status);
65
68
  }
66
69
 
@@ -69,6 +72,7 @@ if (!streamEnabled) {
69
72
  try {
70
73
  parsed = JSON.parse(out);
71
74
  } catch (error) {
75
+ persistStderr(resultPath, `claude output was not JSON: ${error.message}`);
72
76
  process.stderr.write(`claude output was not JSON: ${error.message}\n`);
73
77
  process.exit(1);
74
78
  }
@@ -166,6 +170,7 @@ function renderEvent(ev) {
166
170
 
167
171
  child.on("error", (err) => {
168
172
  render.finishLive(); // restore the terminal before exiting
173
+ persistStderr(resultPath, `claude spawn failed: ${err.message}`);
169
174
  process.stderr.write(`claude spawn failed: ${err.message}\n`);
170
175
  process.exit(1);
171
176
  });
@@ -174,12 +179,15 @@ child.on("close", (code) => {
174
179
  render.finishLive(); // stop the spinner + restore the cursor BEFORE any further output
175
180
  render.writeTranscript(transcriptPath); // full narration + tool I/O always saved
176
181
  if (code !== 0) {
182
+ const detail = childStderr.trim() || `claude exited ${code === null ? "(timeout/killed)" : code}`;
183
+ persistStderr(resultPath, detail);
177
184
  if (childStderr.trim()) process.stderr.write(`${childStderr.trim()}\n`);
178
185
  process.stderr.write(`claude exited ${code === null ? "(timeout/killed)" : code}\n`);
179
186
  process.exit(code === null ? 1 : code);
180
187
  }
181
188
  if (typeof resultText !== "string") {
182
189
  // Fail closed: no result event ⇒ no result.md ⇒ CW records a failed hop.
190
+ persistStderr(resultPath, childStderr.trim() || "claude produced no result event — refusing to fabricate a result");
183
191
  process.stderr.write("claude produced no result event — refusing to fabricate a result\n");
184
192
  process.exit(1);
185
193
  }
@@ -14,6 +14,13 @@
14
14
  //
15
15
  // stdout: one JSON object { model, usage, result } for CW provenance.
16
16
  // stderr: optional live trace when CW_AGENT_STREAM=1 and attached to a TTY.
17
+ //
18
+ // SPEED: codex `exec` inherits the user's ~/.codex/config.toml, including a heavy
19
+ // `model_reasoning_effort` (e.g. "high"), which makes every read/grep turn slow.
20
+ // CW caps it for ITS runs only via `-c model_reasoning_effort=<effort>` — a
21
+ // per-run override that does NOT touch the user's interactive codex. Tune with
22
+ // CW_CODEX_REASONING_EFFORT (default "low"); set it to "medium"/"high" to opt back
23
+ // into more thinking.
17
24
 
18
25
  const fs = require("node:fs");
19
26
  const os = require("node:os");
@@ -25,6 +32,7 @@ const {
25
32
  emitReport,
26
33
  flushJsonLines,
27
34
  parseJsonLines,
35
+ persistStderr,
28
36
  writeResult
29
37
  } = require("./agent-adapter-core");
30
38
 
@@ -74,9 +82,14 @@ function recordJsonLine(line) {
74
82
 
75
83
  render.action("codex: reading the repo (read-only)…");
76
84
 
85
+ // Cap codex's reasoning effort for CW runs (speed) — overrides config.toml for
86
+ // THIS invocation only. Default "low"; CW_CODEX_REASONING_EFFORT opts back up.
87
+ const effort = process.env.CW_CODEX_REASONING_EFFORT || "low";
77
88
  const args = [
78
89
  "exec",
79
90
  "--json",
91
+ "-c",
92
+ `model_reasoning_effort=${effort}`,
80
93
  "--output-last-message",
81
94
  finalPath,
82
95
  "--sandbox",
@@ -108,6 +121,7 @@ child.stderr.on("data", (chunk) => {
108
121
 
109
122
  child.on("error", (error) => {
110
123
  render.finishLive();
124
+ persistStderr(resultPath, `codex spawn failed: ${error.message}`);
111
125
  process.stderr.write(`codex spawn failed: ${error.message}\n`);
112
126
  process.exit(1);
113
127
  });
@@ -118,11 +132,14 @@ child.on("close", (code) => {
118
132
  render.writeTranscript(transcriptPath);
119
133
  if (code !== 0) {
120
134
  const detail = childStderr.trim() || `codex exited ${code === null ? "(timeout/killed)" : code}`;
135
+ persistStderr(resultPath, detail);
121
136
  process.stderr.write(`${detail}\n`);
122
137
  process.exit(code === null ? 1 : code);
123
138
  }
124
139
  if (state.invalidJson) {
125
- process.stderr.write("codex --json produced a non-JSONL stdout line - refusing to trust the result\n");
140
+ const detail = "codex --json produced a non-JSONL stdout line - refusing to trust the result";
141
+ persistStderr(resultPath, childStderr.trim() || detail);
142
+ process.stderr.write(`${detail}\n`);
126
143
  process.exit(1);
127
144
  }
128
145
 
@@ -130,7 +147,9 @@ child.on("close", (code) => {
130
147
  try {
131
148
  resultText = fs.readFileSync(finalPath, "utf8");
132
149
  } catch {
133
- process.stderr.write("codex produced no final output file - refusing to fabricate a result\n");
150
+ const detail = "codex produced no final output file - refusing to fabricate a result";
151
+ persistStderr(resultPath, childStderr.trim() || detail);
152
+ process.stderr.write(`${detail}\n`);
134
153
  process.exit(1);
135
154
  } finally {
136
155
  try {
@@ -143,6 +162,7 @@ child.on("close", (code) => {
143
162
  try {
144
163
  writeResult(resultPath, resultText);
145
164
  } catch (error) {
165
+ persistStderr(resultPath, `codex produced no final result: ${error.message}`);
146
166
  process.stderr.write(`codex produced no final result: ${error.message}\n`);
147
167
  process.exit(1);
148
168
  }
@@ -22,6 +22,7 @@ const {
22
22
  emitReport,
23
23
  flushJsonLines,
24
24
  parseJsonLines,
25
+ persistStderr,
25
26
  writeResult
26
27
  } = require("./agent-adapter-core");
27
28
 
@@ -83,6 +84,7 @@ child.stderr.on("data", (chunk) => {
83
84
 
84
85
  child.on("error", (error) => {
85
86
  render.finishLive();
87
+ persistStderr(resultPath, `gemini spawn failed: ${error.message}`);
86
88
  process.stderr.write(`gemini spawn failed: ${error.message}\n`);
87
89
  process.exit(1);
88
90
  });
@@ -93,23 +95,29 @@ child.on("close", (code) => {
93
95
  render.writeTranscript(transcriptPath);
94
96
  if (code !== 0) {
95
97
  const detail = childStderr.trim() || `gemini exited ${code === null ? "(timeout/killed)" : code}`;
98
+ persistStderr(resultPath, detail);
96
99
  process.stderr.write(`${detail}\n`);
97
100
  process.exit(code === null ? 1 : code);
98
101
  }
99
102
  if (state.invalidJson) {
100
- process.stderr.write("gemini --output-format stream-json produced a non-JSONL stdout line - refusing to trust the result\n");
103
+ const detail = "gemini --output-format stream-json produced a non-JSONL stdout line - refusing to trust the result";
104
+ persistStderr(resultPath, childStderr.trim() || detail);
105
+ process.stderr.write(`${detail}\n`);
101
106
  process.exit(1);
102
107
  }
103
108
 
104
109
  const resultText = state.finalResult || state.textFragments.join("\n\n");
105
110
  if (!resultText.trim()) {
106
- process.stderr.write("gemini produced no result text - refusing to fabricate a result\n");
111
+ const detail = "gemini produced no result text - refusing to fabricate a result";
112
+ persistStderr(resultPath, childStderr.trim() || detail);
113
+ process.stderr.write(`${detail}\n`);
107
114
  process.exit(1);
108
115
  }
109
116
 
110
117
  try {
111
118
  writeResult(resultPath, resultText);
112
119
  } catch (error) {
120
+ persistStderr(resultPath, `gemini produced no final result: ${error.message}`);
113
121
  process.stderr.write(`gemini produced no final result: ${error.message}\n`);
114
122
  process.exit(1);
115
123
  }
@@ -27,6 +27,7 @@ const {
27
27
  emitReport,
28
28
  flushJsonLines,
29
29
  parseJsonLines,
30
+ persistStderr,
30
31
  writeResult
31
32
  } = require("./agent-adapter-core");
32
33
 
@@ -136,6 +137,7 @@ child.stderr.on("data", (chunk) => {
136
137
 
137
138
  child.on("error", (error) => {
138
139
  render.finishLive();
140
+ persistStderr(resultPath, `opencode spawn failed: ${error.message}`);
139
141
  process.stderr.write(`opencode spawn failed: ${error.message}\n`);
140
142
  process.exit(1);
141
143
  });
@@ -146,23 +148,29 @@ child.on("close", (code) => {
146
148
  render.writeTranscript(transcriptPath);
147
149
  if (code !== 0) {
148
150
  const detail = childStderr.trim() || `opencode exited ${code === null ? "(timeout/killed)" : code}`;
151
+ persistStderr(resultPath, detail);
149
152
  process.stderr.write(`${detail}\n`);
150
153
  process.exit(code === null ? 1 : code);
151
154
  }
152
155
  if (state.invalidJson) {
153
- process.stderr.write("opencode --format json produced a non-JSONL stdout line - refusing to trust the result\n");
156
+ const detail = "opencode --format json produced a non-JSONL stdout line - refusing to trust the result";
157
+ persistStderr(resultPath, childStderr.trim() || detail);
158
+ process.stderr.write(`${detail}\n`);
154
159
  process.exit(1);
155
160
  }
156
161
 
157
162
  const resultText = state.finalResult || state.lastMessageText || state.textFragments.join("\n\n");
158
163
  if (!resultText.trim()) {
159
- process.stderr.write("opencode produced no result text - refusing to fabricate a result\n");
164
+ const detail = "opencode produced no result text - refusing to fabricate a result";
165
+ persistStderr(resultPath, childStderr.trim() || detail);
166
+ process.stderr.write(`${detail}\n`);
160
167
  process.exit(1);
161
168
  }
162
169
 
163
170
  try {
164
171
  writeResult(resultPath, resultText);
165
172
  } catch (error) {
173
+ persistStderr(resultPath, `opencode produced no final result: ${error.message}`);
166
174
  process.stderr.write(`opencode produced no final result: ${error.message}\n`);
167
175
  process.exit(1);
168
176
  }
@@ -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.95",
86
+ "Cool Workflow v0.1.96",
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.95");
120
+ assert.equal(summary.version, "0.1.96");
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.95");
127
+ assert.equal(shown.app.version, "0.1.96");
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.95");
138
+ assert.equal(state.workflow.app.version, "0.1.96");
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/orchestrator.js");
8
8
 
9
- const TARGET_VERSION = "0.1.95";
9
+ const TARGET_VERSION = "0.1.96";
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.1.95");
36
+ assert.equal(appValidation.summary.version, "0.1.96");
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.95 end-to-end golden path."
45
+ "Prove the deterministic v0.1.96 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.95");
55
+ assert.equal(state.workflow.app.version, "0.1.96");
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\.95/);
198
+ assert.match(report, /Workflow App: end-to-end-golden-path@0\.1\.96/);
199
199
  assert.match(report, /## Candidates/);
200
200
  assert.match(report, /## Trust Audit/);
201
201
  assert.match(report, /## Acceptance Rationale/);