cool-workflow 0.1.87 → 0.1.89
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/README.md +111 -71
- package/apps/architecture-review/app.json +1 -1
- 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 +42 -1
- package/dist/capability-core.js +16 -4
- package/dist/capability-registry.js +24 -0
- package/dist/cli/command-surface.js +105 -2
- package/dist/cli.js +2 -1
- package/dist/doctor.js +14 -1
- package/dist/drive.js +222 -16
- package/dist/execution-backend.js +4 -4
- package/dist/loop-expansion.js +60 -0
- package/dist/onramp.js +25 -0
- package/dist/orchestrator/lifecycle-operations.js +134 -3
- package/dist/orchestrator.js +48 -77
- package/dist/run-export.js +106 -2
- package/dist/state-node.js +13 -3
- package/dist/state.js +21 -0
- package/dist/telemetry-attestation.js +30 -6
- package/dist/telemetry-demo.js +29 -1
- package/dist/telemetry-ledger.js +6 -0
- package/dist/version.js +1 -1
- package/dist/worker-accept/telemetry-ledger.js +12 -2
- package/dist/workflow-api.js +33 -0
- package/dist/workflow-app-framework.js +20 -0
- package/docs/agent-delegation-drive.7.md +8 -0
- package/docs/capability-topology-registry.7.md +69 -46
- package/docs/cli-mcp-parity.7.md +16 -2
- package/docs/contract-migration-tooling.7.md +8 -0
- package/docs/control-plane-scheduling.7.md +8 -0
- package/docs/durable-state-and-locking.7.md +8 -0
- package/docs/evidence-adoption-reasoning-chain.7.md +8 -0
- package/docs/execution-backends.7.md +8 -0
- package/docs/launch/launch-kit.md +9 -9
- package/docs/multi-agent-cli-mcp-surface.7.md +8 -0
- package/docs/multi-agent-eval-replay-harness.7.md +8 -0
- package/docs/multi-agent-operator-ux.7.md +8 -0
- package/docs/node-snapshot-diff-replay.7.md +8 -0
- package/docs/observability-cost-accounting.7.md +8 -0
- package/docs/project-index.md +22 -5
- package/docs/readme-v0.1.87-full.md +301 -0
- package/docs/real-execution-backends.7.md +8 -0
- package/docs/release-and-migration.7.md +8 -0
- package/docs/release-tooling.7.md +8 -0
- package/docs/report-verifiable-bundle.7.md +34 -2
- package/docs/run-registry-control-plane.7.md +18 -0
- package/docs/run-retention-reclamation.7.md +8 -0
- package/docs/state-explosion-management.7.md +8 -0
- package/docs/team-collaboration.7.md +8 -0
- package/docs/trust-model.md +6 -4
- package/docs/web-desktop-workbench.7.md +8 -0
- package/manifest/plugin.manifest.json +1 -1
- package/manifest/source-context-profiles.json +1 -1
- package/package.json +8 -5
- package/scripts/agents/agent-adapter-core.js +1 -1
- package/scripts/agents/builtin-templates.json +2 -1
- package/scripts/agents/claude-p-agent.js +7 -33
- package/scripts/agents/codex-agent.js +1 -1
- package/scripts/agents/cw-attest-wrap.js +9 -1
- package/scripts/agents/gemini-agent.js +1 -1
- package/scripts/agents/opencode-agent.js +1 -1
- package/scripts/canonical-apps.js +4 -4
- package/scripts/coverage-gate.js +15 -1
- package/scripts/cw.js +0 -0
- package/scripts/dogfood-release.js +1 -1
- package/scripts/golden-path.js +4 -4
- package/scripts/release-flow.js +49 -2
- package/scripts/release-gate.sh +11 -2
- package/tsconfig.json +3 -1
|
@@ -79,7 +79,7 @@ child.stdout.on("data", (chunk) => {
|
|
|
79
79
|
child.stderr.setEncoding("utf8");
|
|
80
80
|
child.stderr.on("data", (chunk) => {
|
|
81
81
|
childStderr += chunk;
|
|
82
|
-
if (process.env.CW_AGENT_STREAM
|
|
82
|
+
if (process.env.CW_AGENT_STREAM !== "0" && process.env.CW_NO_STREAM !== "1" && process.stderr.isTTY) {
|
|
83
83
|
process.stderr.write(chunk);
|
|
84
84
|
}
|
|
85
85
|
});
|
|
@@ -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.89",
|
|
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.89");
|
|
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.89");
|
|
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.89");
|
|
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`);
|
package/scripts/coverage-gate.js
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
//
|
|
26
26
|
// Usage: node scripts/coverage-gate.js [--min 80] [--concurrency <n|auto>]
|
|
27
27
|
|
|
28
|
-
const { spawn } = require("node:child_process");
|
|
28
|
+
const { spawn, spawnSync } = require("node:child_process");
|
|
29
29
|
const fs = require("node:fs");
|
|
30
30
|
const os = require("node:os");
|
|
31
31
|
const path = require("node:path");
|
|
@@ -50,11 +50,25 @@ if (!Number.isFinite(floor) || floor < 0 || floor > 100) {
|
|
|
50
50
|
|
|
51
51
|
const covDir = fs.mkdtempSync(path.join(os.tmpdir(), "cw-coverage-"));
|
|
52
52
|
|
|
53
|
+
// Pre-check: ensure dist/ is built before entering the coverage merge phase
|
|
54
|
+
// (parity with the `test` and `test:ci` package.json scripts).
|
|
55
|
+
{
|
|
56
|
+
const cli = path.join(packageDir, "dist", "cli.js");
|
|
57
|
+
const check = spawnSync(process.execPath, [cli, "version"], { cwd: packageDir, stdio: "pipe", encoding: "utf8" });
|
|
58
|
+
const out = String(check.stdout || "").trim();
|
|
59
|
+
if (check.status !== 0 || !out) {
|
|
60
|
+
process.stderr.write(`${SELF}: dist/cli.js version failed (exit ${check.status}) — build may be stale. Run \`npm run build\` first.\n`);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
53
65
|
function runSuite() {
|
|
54
66
|
return new Promise((resolve) => {
|
|
55
67
|
const args = [path.join(packageDir, "test", "run-all.js")];
|
|
56
68
|
const concurrency = flagValue("--concurrency");
|
|
57
69
|
if (concurrency) args.push("--concurrency", concurrency);
|
|
70
|
+
const sample = flagValue("--sample");
|
|
71
|
+
if (sample) args.push("--sample", sample);
|
|
58
72
|
const child = spawn(process.execPath, args, {
|
|
59
73
|
cwd: packageDir,
|
|
60
74
|
stdio: "inherit",
|
package/scripts/cw.js
CHANGED
|
File without changes
|
|
@@ -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.89";
|
|
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.89");
|
|
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.89 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.89");
|
|
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\.89/);
|
|
199
199
|
assert.match(report, /## Candidates/);
|
|
200
200
|
assert.match(report, /## Trust Audit/);
|
|
201
201
|
assert.match(report, /## Acceptance Rationale/);
|
package/scripts/release-flow.js
CHANGED
|
@@ -25,6 +25,12 @@
|
|
|
25
25
|
// --soft for best-effort (skip-not-fail).
|
|
26
26
|
// Flags also accepted: --prev-tag <t>, --agent-command "...", --agent-model m, --dry-run
|
|
27
27
|
//
|
|
28
|
+
// VERDICT CONTRACT (auto-generated by --cut, never write by hand):
|
|
29
|
+
// Path: .cw-release/review-<FULLSHA>.verdict
|
|
30
|
+
// First line: "APPROVED <FULLSHA>"
|
|
31
|
+
// Both release-flow.js --cut and CI's release-gate.yml verify this exact path and
|
|
32
|
+
// format. The file MUST be committed to the tag's history.
|
|
33
|
+
//
|
|
28
34
|
// Test seams (smoke/operator only, never the delegated agent):
|
|
29
35
|
// CW_RELEASE_FLOW_GATE_CMD overrides the deterministic gate command
|
|
30
36
|
// (default: `bash <thisdir>/release-gate.sh`) so the smoke can exercise the
|
|
@@ -153,6 +159,32 @@ function substitute(arg, map) {
|
|
|
153
159
|
return arg.replace(/\{\{(\w+)\}\}/g, (m, k) => (k in map ? String(map[k]) : m));
|
|
154
160
|
}
|
|
155
161
|
|
|
162
|
+
/** Extract verdict lines from agent stdout. Returns the verdict text (first line
|
|
163
|
+
* must be APPROVED <sha> or REJECTED) or null if no valid verdict found.
|
|
164
|
+
* Also logs failures to stderr so the operator can inspect. */
|
|
165
|
+
function extractVerdictFromStdout(stdout, resultPath) {
|
|
166
|
+
const lines = stdout.split(/\r?\n/);
|
|
167
|
+
const approvedLine = lines.find((line) => /^APPROVED\s+\S+/.test(line.trim()));
|
|
168
|
+
if (approvedLine) {
|
|
169
|
+
const idx = lines.indexOf(approvedLine);
|
|
170
|
+
// Include the APPROVED line and any capability line that follows.
|
|
171
|
+
const capLine = (lines[idx + 1] || "").trim();
|
|
172
|
+
const verdict = capLine && !capLine.startsWith("#") && !/^(APPROVED|REJECTED)\s/.test(capLine)
|
|
173
|
+
? [approvedLine.trim(), capLine].join("\n")
|
|
174
|
+
: approvedLine.trim();
|
|
175
|
+
say(`reviewer verdict captured from stdout → ${resultPath}`);
|
|
176
|
+
return verdict;
|
|
177
|
+
}
|
|
178
|
+
const rejectedLine = lines.find((line) => /^REJECTED/i.test(line.trim()));
|
|
179
|
+
if (rejectedLine) {
|
|
180
|
+
process.stderr.write(`reviewer REJECTED via stdout — full output:\n${stdout.trim()}\n`);
|
|
181
|
+
return rejectedLine.trim();
|
|
182
|
+
}
|
|
183
|
+
// Partial: print the last 20 lines to help the operator diagnose.
|
|
184
|
+
process.stderr.write(`reviewer stdout had no APPROVED/REJECTED line. Last 20 lines:\n${lines.slice(-20).join("\n").trim()}\n`);
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
|
|
156
188
|
function delegateReview(resultPath, inputPath) {
|
|
157
189
|
// Reuse the canonical agent-config resolver (flags > env > file).
|
|
158
190
|
let resolveAgentConfig;
|
|
@@ -188,16 +220,31 @@ function delegateReview(resultPath, inputPath) {
|
|
|
188
220
|
say(`[2/3] reviewer — delegating to: ${cfg.command} ${(cfg.args || []).join(" ")} (model: ${cfg.model || "unreported"})`);
|
|
189
221
|
// RED LINE: argv-style, shell:false. The agent runs the model in its own
|
|
190
222
|
// process and inherits its own credentials; CW holds none.
|
|
223
|
+
// Capture stdout so agents that print verdicts (rather than writing the
|
|
224
|
+
// result file) are supported. Agents that DO write the file still work:
|
|
225
|
+
// the file takes precedence. stderr goes to the terminal for live output.
|
|
191
226
|
const r = spawnSync(cfg.command, args, {
|
|
192
227
|
cwd: repoRoot,
|
|
193
228
|
env: { ...process.env },
|
|
194
229
|
encoding: "utf8",
|
|
195
230
|
timeout: cfg.timeoutMs || 600000,
|
|
196
231
|
shell: false,
|
|
197
|
-
stdio: "inherit"
|
|
232
|
+
stdio: ["ignore", "pipe", "inherit"],
|
|
233
|
+
maxBuffer: 32 * 1024 * 1024
|
|
198
234
|
});
|
|
199
235
|
if (r.status !== 0) die(`reviewer agent exited ${r.status === null ? "(timeout/no-exit)" : r.status} — no verdict trusted.`);
|
|
200
|
-
|
|
236
|
+
// If the agent already wrote the verdict file (backward compat), use it.
|
|
237
|
+
// Otherwise extract the verdict from stdout (headless agent path).
|
|
238
|
+
if (fs.existsSync(resultPath)) return;
|
|
239
|
+
const stdout = String(r.stdout || "").trim();
|
|
240
|
+
if (stdout) {
|
|
241
|
+
const verdictLines = extractVerdictFromStdout(stdout, resultPath);
|
|
242
|
+
if (verdictLines) {
|
|
243
|
+
fs.writeFileSync(resultPath, `${verdictLines}\n`);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
die("reviewer agent produced no verdict — stdout had no APPROVED <sha> or REJECTED line, and no verdict file was written. Ensure the agent writes the verdict to {{result}} or prints the verdict lines to stdout.");
|
|
201
248
|
}
|
|
202
249
|
|
|
203
250
|
// Endpoint mode (e.g. DeepSeek HTTP): POST the prompt, write the response as
|
package/scripts/release-gate.sh
CHANGED
|
@@ -51,7 +51,7 @@ if [[ -n "$PREV_TAG" ]]; then
|
|
|
51
51
|
TESTS_CHANGED=$(git diff --name-only "$RANGE" | grep -cE '\.(test|spec)\.|/tests?/' || true)
|
|
52
52
|
[[ "$TESTS_CHANGED" -gt 0 ]] || fail "zero test changes since $PREV_TAG"
|
|
53
53
|
|
|
54
|
-
# --- 4. Cadence: >=4 cycles logged OR >=24h since previous tag
|
|
54
|
+
# --- 4. Cadence: >=4 cycles logged OR >=24h since previous tag, or a recorded HOTFIX ---
|
|
55
55
|
say "[5/6] cadence"
|
|
56
56
|
CYCLES=0
|
|
57
57
|
if [[ -f ITERATION_LOG.md && -n "$PREV_TAG" ]]; then
|
|
@@ -60,8 +60,17 @@ if [[ -n "$PREV_TAG" ]]; then
|
|
|
60
60
|
PREV_TS=$(git log -1 --format=%ct "$PREV_TAG")
|
|
61
61
|
NOW_TS=$(date +%s)
|
|
62
62
|
HOURS=$(( (NOW_TS - PREV_TS) / 3600 ))
|
|
63
|
+
# Hotfix path: an urgent fix may ship inside the cadence window, but ONLY via an
|
|
64
|
+
# EXPLICIT, RECORDED declaration — a "HOTFIX:" line added to ITERATION_LOG.md in this
|
|
65
|
+
# release range, carrying a reason. It is committed (auditable in the tag's history)
|
|
66
|
+
# and echoed here, so the bypass is never silent and a reviewer sees the reason.
|
|
67
|
+
HOTFIX="$(git diff "$RANGE" -- ITERATION_LOG.md | grep -E '^\+.*HOTFIX:' | head -1 | sed -E 's/^\+[[:space:]]*//' || true)"
|
|
63
68
|
if [[ "$CYCLES" -lt 4 && "$HOURS" -lt 24 ]]; then
|
|
64
|
-
|
|
69
|
+
if [[ -n "$HOTFIX" ]]; then
|
|
70
|
+
say " cadence bypassed by recorded HOTFIX (${HOURS}h, ${CYCLES} cycle-lines): ${HOTFIX}"
|
|
71
|
+
else
|
|
72
|
+
fail "cadence: only $CYCLES cycles logged and ${HOURS}h since $PREV_TAG (need >=4 cycles, >=24h, or a recorded 'HOTFIX:' line in ITERATION_LOG.md)"
|
|
73
|
+
fi
|
|
65
74
|
fi
|
|
66
75
|
else
|
|
67
76
|
say "[3-5/6] no previous tag; substance/evidence/cadence checks skipped"
|
package/tsconfig.json
CHANGED