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.
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/apps/architecture-review/app.json +1 -1
- package/apps/architecture-review/workflow.js +3 -3
- package/apps/architecture-review-fast/app.json +1 -1
- package/apps/end-to-end-golden-path/app.json +1 -1
- package/apps/pr-review-fix-ci/app.json +1 -1
- package/apps/release-cut/app.json +1 -1
- package/apps/research-synthesis/app.json +1 -1
- package/dist/agent-config.js +2 -1
- package/dist/dispatch.js +12 -6
- package/dist/evidence-grounding.js +18 -13
- package/dist/execution-backend/probes.js +22 -6
- package/dist/execution-backend.js +37 -4
- package/dist/node-snapshot.js +3 -3
- package/dist/orchestrator/lifecycle-operations.js +13 -5
- package/dist/orchestrator.js +26 -1
- package/dist/reclamation.js +8 -2
- package/dist/run-registry/derive.js +4 -1
- package/dist/scheduler.js +14 -14
- package/dist/schema-validate.js +8 -2
- package/dist/state-explosion/helpers.js +4 -21
- package/dist/state-explosion/size.js +63 -0
- package/dist/state-explosion.js +18 -71
- package/dist/state.js +47 -9
- package/dist/trust-audit.js +27 -2
- package/dist/util/fingerprint.js +19 -0
- package/dist/util/fingerprint.test.js +27 -0
- package/dist/version.js +1 -1
- package/dist/workbench-host.js +11 -0
- package/dist/workbench.js +19 -17
- package/dist/worker-isolation.js +25 -1
- package/docs/agent-delegation-drive.7.md +64 -1
- package/docs/cli-mcp-parity.7.md +2 -0
- package/docs/contract-migration-tooling.7.md +2 -0
- package/docs/control-plane-scheduling.7.md +2 -0
- package/docs/durable-state-and-locking.7.md +2 -0
- package/docs/evidence-adoption-reasoning-chain.7.md +2 -0
- package/docs/execution-backends.7.md +2 -0
- package/docs/multi-agent-cli-mcp-surface.7.md +2 -0
- package/docs/multi-agent-eval-replay-harness.7.md +2 -0
- package/docs/multi-agent-operator-ux.7.md +2 -0
- package/docs/node-snapshot-diff-replay.7.md +2 -0
- package/docs/observability-cost-accounting.7.md +2 -0
- package/docs/project-index.md +7 -3
- package/docs/real-execution-backends.7.md +2 -0
- package/docs/release-and-migration.7.md +2 -0
- package/docs/release-tooling.7.md +18 -0
- package/docs/routines.md +30 -0
- package/docs/run-registry-control-plane.7.md +2 -0
- package/docs/run-retention-reclamation.7.md +2 -0
- package/docs/sandbox-profiles.7.md +15 -0
- package/docs/state-explosion-management.7.md +2 -0
- package/docs/team-collaboration.7.md +2 -0
- package/docs/web-desktop-workbench.7.md +2 -0
- package/manifest/plugin.manifest.json +1 -1
- package/package.json +5 -3
- package/scripts/agents/agent-adapter-core.js +22 -1
- package/scripts/agents/claude-p-agent.js +10 -2
- package/scripts/agents/codex-agent.js +22 -2
- package/scripts/agents/gemini-agent.js +10 -2
- package/scripts/agents/opencode-agent.js +10 -2
- package/scripts/canonical-apps.js +4 -4
- package/scripts/dogfood-release.js +1 -1
- package/scripts/golden-path.js +4 -4
- package/scripts/release-flow.js +10 -0
- package/scripts/release-gate.sh +1 -1
package/docs/cli-mcp-parity.7.md
CHANGED
package/docs/project-index.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# Cool Workflow Project Index
|
|
2
2
|
|
|
3
|
-
Generated from the current repository code on 2026-06-
|
|
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.
|
|
8
|
+
- Version: `0.1.96`
|
|
9
9
|
- Source modules: `68`
|
|
10
10
|
- Workflow apps: `8`
|
|
11
11
|
- Docs: `53`
|
|
12
|
-
- Smoke tests: `
|
|
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)
|
|
@@ -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
|
|
@@ -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
|
|
@@ -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.
|
|
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.
|
|
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 --
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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, "..", "..");
|
package/scripts/golden-path.js
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
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\.
|
|
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/);
|