cool-workflow 0.1.78 → 0.1.79
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 +21 -3
- package/apps/architecture-review/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/capability-core.js +33 -0
- package/dist/capability-registry.js +2 -0
- package/dist/cli.js +39 -0
- package/dist/mcp-server.js +8 -0
- package/dist/telemetry-demo.js +154 -0
- package/dist/version.js +1 -1
- package/docs/agent-delegation-drive.7.md +2 -0
- 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/launch/launch-kit.md +116 -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 +5 -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 +2 -0
- package/docs/run-registry-control-plane.7.md +2 -0
- package/docs/run-retention-reclamation.7.md +2 -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 +1 -1
- package/scripts/canonical-apps.js +4 -4
- package/scripts/dogfood-release.js +1 -1
- package/scripts/golden-path.js +4 -4
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cool-workflow",
|
|
3
3
|
"description": "Auditable workflow control-plane and orchestration runtime: TypeScript dispatch, evidence-gated verification, state commits, scheduling, routines, multi-agent coordination, and MCP. Delegates execution to external agents — never runs models.",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.79",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "COOLWHITE LLC"
|
|
7
7
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cool-workflow",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.79",
|
|
4
4
|
"description": "Auditable workflow control-plane and orchestration runtime: TypeScript dispatch, evidence-gated verification, state commits, scheduling, routines, multi-agent coordination, and MCP. Delegates execution to external agents — never runs models.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "COOLWHITE LLC"
|
package/README.md
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
```
|
|
9
9
|
|
|
10
10
|
[](https://github.com/coo1white/cool-workflow/actions/workflows/ci.yml)
|
|
11
|
+
[](https://www.npmjs.com/package/cool-workflow)
|
|
12
|
+
[](https://www.npmjs.com/package/cool-workflow)
|
|
11
13
|
[](https://github.com/coo1white/cool-workflow/tags)
|
|
12
14
|
[](../../LICENSE)
|
|
13
15
|

|
|
@@ -631,8 +633,24 @@ CHANGELOG.md and RELEASE.md are content surfaces checked by the dogfood-release
|
|
|
631
633
|
|
|
632
634
|
Auto-compaction hook moved from `saveCheckpoint()` to explicit `maybeCompactRun()` calls after major lifecycle mutations. Fixes test fixture fingerprint instability. Also fixes the dogfood-release version-sync pipeline: always use `npm run bump:version`, never hand-edit version.ts alone.
|
|
633
635
|
|
|
634
|
-
v0.1.76
|
|
636
|
+
## Control-plane naming (v0.1.76)
|
|
635
637
|
|
|
636
|
-
|
|
638
|
+
Positioning consistency: every self-describing surface names CW an auditable workflow control-plane / Workflow App framework, not an "SDK" (which survives only in the red-line disclaimer "embeds no model SDK").
|
|
637
639
|
|
|
638
|
-
v0.1.
|
|
640
|
+
## Workflow orchestration: Tracks 1–3 (v0.1.77)
|
|
641
|
+
|
|
642
|
+
The orchestration vision landed in one release, all reviewer-gated:
|
|
643
|
+
|
|
644
|
+
- **Track 1 — telemetry attestation**: each agent's reported token usage is verified against an operator ed25519 trust key (`attested`/`unattested`/`absent`, surfaced loudly), recorded in a tamper-evident hash-chained ledger; opt-in `require-attested-telemetry` fails closed on unverifiable usage.
|
|
645
|
+
- **Track 2 — concurrent failure semantics**: a `parallel()` phase runs its agents concurrently with declared collapse rules — **collect-all** (a failing hop never aborts siblings) and **kill-on-timeout** (a hung agent is killed at its deadline and counted as one failure). 16 agents with a forced hang + crash + dirty-return complete with no deadlock and a replay-complete record.
|
|
646
|
+
- **Track 3 — boundary contract**: per-task output `schema` validation (dependency-free, parks on mismatch), `limits.tokenBudget` enforced against recorded usage, and the one-way executor boundary welded into the type layer (a callable crossing it fails `npm run build`).
|
|
647
|
+
|
|
648
|
+
## Working onboarding + npm distribution (v0.1.78)
|
|
649
|
+
|
|
650
|
+
`--agent-command builtin:claude` resolves to a bundled read-only claude wrapper that completes workers with a real agent; the cross-directory quickstart crash is fixed; missing optional inputs no longer leak `{{name}}` into prompts. Published to npm (`cool-workflow`, bins `cw`/`cool-workflow`) with LICENSE and metadata. Live dogfood proof committed under `docs/dogfood/`.
|
|
651
|
+
|
|
652
|
+
## Tamper-evidence demo (on main, ships next)
|
|
653
|
+
|
|
654
|
+
`cw demo tamper` — a hermetic, one-command proof that a recorded telemetry verdict cannot be forged undetected: it builds a real ed25519-signed ledger, forges it at the ledger layer (verdict flip + recomputed local hash → the chain still breaks) and the signature layer (inflated tokens, reused signature → ed25519 rejects), all verified offline with only the public key. `cw telemetry verify <run>` is the operator-facing half (`cw_telemetry_verify` on MCP).
|
|
655
|
+
|
|
656
|
+
v0.1.79
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"id": "architecture-review",
|
|
4
4
|
"title": "Architecture Review",
|
|
5
5
|
"summary": "Map a repository architecture, assess risks, verify important findings, and synthesize an evidence-backed verdict.",
|
|
6
|
-
"version": "0.1.
|
|
6
|
+
"version": "0.1.79",
|
|
7
7
|
"author": "COOLWHITE LLC",
|
|
8
8
|
"inputs": [
|
|
9
9
|
{
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"id": "pr-review-fix-ci",
|
|
4
4
|
"title": "PR Review Fix CI",
|
|
5
5
|
"summary": "Review a pull request or branch, inspect CI failures, diagnose actionable issues, optionally patch, verify, and summarize with evidence.",
|
|
6
|
-
"version": "0.1.
|
|
6
|
+
"version": "0.1.79",
|
|
7
7
|
"author": "COOLWHITE LLC",
|
|
8
8
|
"inputs": [
|
|
9
9
|
{
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"id": "release-cut",
|
|
4
4
|
"title": "Release Cut",
|
|
5
5
|
"summary": "Prepare a release with checklist discipline: version checks, changelog, tests, packaging, release notes, and final verification.",
|
|
6
|
-
"version": "0.1.
|
|
6
|
+
"version": "0.1.79",
|
|
7
7
|
"author": "COOLWHITE LLC",
|
|
8
8
|
"inputs": [
|
|
9
9
|
{
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"id": "research-synthesis",
|
|
4
4
|
"title": "Research Synthesis",
|
|
5
5
|
"summary": "Split a research question into claims, investigate sources, cross-check evidence, verify claims, and synthesize a concise answer.",
|
|
6
|
-
"version": "0.1.
|
|
6
|
+
"version": "0.1.79",
|
|
7
7
|
"author": "COOLWHITE LLC",
|
|
8
8
|
"inputs": [
|
|
9
9
|
{
|
package/dist/capability-core.js
CHANGED
|
@@ -61,11 +61,15 @@ exports.sandboxProfileIdFrom = sandboxProfileIdFrom;
|
|
|
61
61
|
exports.withoutRuntimeKeys = withoutRuntimeKeys;
|
|
62
62
|
exports.optionalString = optionalString;
|
|
63
63
|
exports.isRecord = isRecord;
|
|
64
|
+
exports.telemetryVerify = telemetryVerify;
|
|
65
|
+
exports.demoTamper = demoTamper;
|
|
64
66
|
const capability_registry_1 = require("./capability-registry");
|
|
65
67
|
const drive_1 = require("./drive");
|
|
66
68
|
const agent_config_1 = require("./agent-config");
|
|
67
69
|
const run_registry_1 = require("./run-registry");
|
|
68
70
|
const observability_1 = require("./observability");
|
|
71
|
+
const telemetry_ledger_1 = require("./telemetry-ledger");
|
|
72
|
+
const telemetry_demo_1 = require("./telemetry-demo");
|
|
69
73
|
const state_1 = require("./state");
|
|
70
74
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
71
75
|
const node_path_1 = __importDefault(require("node:path"));
|
|
@@ -628,3 +632,32 @@ function optionalString(value) {
|
|
|
628
632
|
function isRecord(value) {
|
|
629
633
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
630
634
|
}
|
|
635
|
+
// ---- telemetry attestation: read-only ledger verification (Track 1) --------
|
|
636
|
+
// Re-prove a run's telemetry chain offline: prevHash linkage + independent per-
|
|
637
|
+
// record hash recompute (never trusts the stored hash). The auditable claim made
|
|
638
|
+
// inspectable on demand — anyone can run this; a forged/edited record fails it.
|
|
639
|
+
function telemetryVerify(runner, args) {
|
|
640
|
+
const runId = optionalString(args.runId || args.run);
|
|
641
|
+
if (!runId)
|
|
642
|
+
throw new Error("telemetry verify requires a run id (cw telemetry verify <run-id>)");
|
|
643
|
+
const run = runner.loadRun(runId);
|
|
644
|
+
const v = (0, telemetry_ledger_1.verifyTelemetryLedger)(run);
|
|
645
|
+
return {
|
|
646
|
+
schemaVersion: 1,
|
|
647
|
+
runId: run.id,
|
|
648
|
+
present: v.present,
|
|
649
|
+
verified: v.verified,
|
|
650
|
+
records: v.records.length,
|
|
651
|
+
attested: v.attested,
|
|
652
|
+
unattested: v.unattested,
|
|
653
|
+
absent: v.absent,
|
|
654
|
+
failedChecks: v.checks.filter((c) => !c.pass).map((c) => ({ name: c.name, code: c.code }))
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
// ---- demo: tamper-evidence (the one-command proof) -------------------------
|
|
658
|
+
// Hermetic, deterministic-shape: builds a real ed25519-signed telemetry ledger,
|
|
659
|
+
// then forges it two ways and shows both tamper-evidence layers catch it. CLI-only
|
|
660
|
+
// (a human-facing demonstration; the underlying verify is the telemetry.verify verb).
|
|
661
|
+
function demoTamper(_runner, _args = {}) {
|
|
662
|
+
return (0, telemetry_demo_1.runTamperDemo)();
|
|
663
|
+
}
|
|
@@ -374,6 +374,8 @@ const BUILTIN_CAPABILITIES = [
|
|
|
374
374
|
{ capability: "gc.plan", summary: "Dry-run plan of run reclamation (per-kind bytes + capability downgrade); frees nothing.", entry: "gcPlan", surface: "both", cli: { path: ["gc", "plan"], caseTokens: ["gc", "plan"], jsonMode: "flag" }, mcp: { tool: "cw_gc_plan" } },
|
|
375
375
|
{ capability: "gc.run", summary: "Execute the write-ahead reclamation transaction (skeleton -> tombstone -> fsync -> free).", entry: "gcRun", surface: "both", cli: { path: ["gc", "run"], caseTokens: ["gc", "run"], jsonMode: "flag" }, mcp: { tool: "cw_gc_run" }, payloadIdentical: false, reason: "Mutating: frees disk and appends a tombstone; both surfaces perform the identical transaction but the payload reports now-derived bytesFreed/tombstone." },
|
|
376
376
|
{ capability: "gc.verify", summary: "Re-prove a reclaimed run: skeleton-complete, tombstone chain untampered, artifacts reconstructable.", entry: "gcVerify", surface: "both", cli: { path: ["gc", "verify"], caseTokens: ["gc", "verify"], jsonMode: "flag" }, mcp: { tool: "cw_gc_verify" } },
|
|
377
|
+
{ capability: "telemetry.verify", summary: "Re-prove a run's telemetry attestation ledger offline (chain linkage + independent hash recompute).", entry: "telemetryVerify", surface: "both", cli: { path: ["telemetry", "verify"], caseTokens: ["telemetry"], jsonMode: "flag" }, mcp: { tool: "cw_telemetry_verify" } },
|
|
378
|
+
{ capability: "demo.tamper", summary: "Prove tamper-evidence: build a signed telemetry ledger, forge it, watch verification fail offline.", entry: "demoTamper", surface: "cli-only", cli: { path: ["demo", "tamper"], caseTokens: ["demo", "tamper"], jsonMode: "flag" }, reason: "Human-facing demonstration (operator/newcomer onboarding); the underlying integrity check is exposed programmatically as the both-surface telemetry.verify. No agent or MCP client needs to invoke a demo." },
|
|
377
379
|
{ capability: "history", summary: "Read a cross-repo unified run timeline (newest first).", entry: "runRegistry.history", surface: "both", cli: { path: ["history"], jsonMode: "flag" }, mcp: { tool: "cw_history" } },
|
|
378
380
|
// ---- web / desktop workbench (v0.1.30) ----------------------------------
|
|
379
381
|
// A THIRD FRONT DOOR — a read-only renderer, not a new brain. Both verbs route
|
package/dist/cli.js
CHANGED
|
@@ -10,6 +10,7 @@ const orchestrator_1 = require("./orchestrator");
|
|
|
10
10
|
const capability_registry_1 = require("./capability-registry");
|
|
11
11
|
const capability_core_1 = require("./capability-core");
|
|
12
12
|
const observability_1 = require("./observability");
|
|
13
|
+
const telemetry_demo_1 = require("./telemetry-demo");
|
|
13
14
|
const run_registry_1 = require("./run-registry");
|
|
14
15
|
const daemon_1 = require("./daemon");
|
|
15
16
|
const scheduler_1 = require("./scheduler");
|
|
@@ -1179,6 +1180,44 @@ async function main() {
|
|
|
1179
1180
|
process.stdout.write(`${(0, run_registry_1.formatHistory)(result)}\n`);
|
|
1180
1181
|
return;
|
|
1181
1182
|
}
|
|
1183
|
+
case "telemetry": {
|
|
1184
|
+
const [subcommand, id] = args.positionals;
|
|
1185
|
+
switch (subcommand) {
|
|
1186
|
+
case "verify": {
|
|
1187
|
+
const result = (0, capability_core_1.telemetryVerify)(runner, { ...args.options, runId: id || args.options.runId || args.options.run });
|
|
1188
|
+
if (wantsJson(args.options))
|
|
1189
|
+
printJson(result);
|
|
1190
|
+
else
|
|
1191
|
+
process.stdout.write(`${(0, telemetry_demo_1.formatTelemetryVerify)(result)}\n`);
|
|
1192
|
+
return;
|
|
1193
|
+
}
|
|
1194
|
+
default:
|
|
1195
|
+
if (await tryDispatchCli(args, runner))
|
|
1196
|
+
return;
|
|
1197
|
+
throw new Error("Usage: cw.js telemetry verify <run-id> [--json]");
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
case "demo": {
|
|
1201
|
+
const [subcommand] = args.positionals;
|
|
1202
|
+
switch (subcommand) {
|
|
1203
|
+
case "tamper": {
|
|
1204
|
+
const result = (0, capability_core_1.demoTamper)(runner, args.options);
|
|
1205
|
+
if (wantsJson(args.options))
|
|
1206
|
+
printJson(result);
|
|
1207
|
+
else
|
|
1208
|
+
process.stdout.write(`${(0, telemetry_demo_1.formatTamperDemo)(result)}\n`);
|
|
1209
|
+
// Fail closed: if the proof did not hold (a tamper went undetected),
|
|
1210
|
+
// exit nonzero so the demo can never green a broken guarantee.
|
|
1211
|
+
if (!result.proven)
|
|
1212
|
+
process.exitCode = 1;
|
|
1213
|
+
return;
|
|
1214
|
+
}
|
|
1215
|
+
default:
|
|
1216
|
+
if (await tryDispatchCli(args, runner))
|
|
1217
|
+
return;
|
|
1218
|
+
throw new Error("Usage: cw.js demo tamper [--json]");
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1182
1221
|
case "workbench": {
|
|
1183
1222
|
const [subcommand, runId] = args.positionals;
|
|
1184
1223
|
switch (subcommand) {
|
package/dist/mcp-server.js
CHANGED
|
@@ -425,6 +425,8 @@ function callTool(name, args) {
|
|
|
425
425
|
return (0, capability_core_1.gcRun)((0, capability_core_1.runRegistryFor)(args, runner), (0, capability_core_1.optionalString)(args.runId), args);
|
|
426
426
|
case "cw_gc_verify":
|
|
427
427
|
return (0, capability_core_1.gcVerify)((0, capability_core_1.runRegistryFor)(args, runner), String(args.runId || ""), args);
|
|
428
|
+
case "cw_telemetry_verify":
|
|
429
|
+
return (0, capability_core_1.telemetryVerify)(runner, args);
|
|
428
430
|
case "cw_history":
|
|
429
431
|
return (0, capability_core_1.runHistory)((0, capability_core_1.runRegistryFor)(args, runner), args);
|
|
430
432
|
case "cw_workbench_view":
|
|
@@ -516,6 +518,8 @@ function requiredArgsForTool(name) {
|
|
|
516
518
|
return ["runId|olderThanDays"];
|
|
517
519
|
if (name === "cw_gc_verify")
|
|
518
520
|
return ["runId"];
|
|
521
|
+
if (name === "cw_telemetry_verify")
|
|
522
|
+
return ["runId"];
|
|
519
523
|
if (name === "cw_queue_show")
|
|
520
524
|
return ["id"];
|
|
521
525
|
if (name.endsWith("_show")) {
|
|
@@ -1531,6 +1535,10 @@ function toolDefinitions() {
|
|
|
1531
1535
|
scope: stringSchema("home (default, cross-repo) or repo"),
|
|
1532
1536
|
runId: stringSchema("Run id to verify")
|
|
1533
1537
|
}),
|
|
1538
|
+
tool("cw_telemetry_verify", "Re-prove a run's telemetry attestation ledger offline: prevHash chain linkage + independent per-record hash recompute (never trusts the stored hash). A forged or edited record fails it. Peer of `cw telemetry verify`.", {
|
|
1539
|
+
cwd: stringSchema("Repo workspace"),
|
|
1540
|
+
runId: stringSchema("Run id to verify")
|
|
1541
|
+
}),
|
|
1534
1542
|
tool("cw_history", "Read a cross-repo unified run timeline (newest first), deterministic and paginated, with provenance links.", {
|
|
1535
1543
|
cwd: stringSchema("Repo workspace"),
|
|
1536
1544
|
scope: stringSchema("home (default, cross-repo) or repo"),
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Tamper-evidence demo (the one-command proof) — make CW's central claim VISIBLE:
|
|
3
|
+
// an audit record proves its own integrity, and ANYONE can re-verify it offline
|
|
4
|
+
// with only the public key. No competitor's pipeline telemetry can do this.
|
|
5
|
+
//
|
|
6
|
+
// Fully hermetic + deterministic: generates an EPHEMERAL ed25519 keypair, builds
|
|
7
|
+
// a REAL telemetry ledger through the production append API (appendTelemetryAttestation
|
|
8
|
+
// + signTelemetry — byte-identical to what a live attested run writes), then
|
|
9
|
+
// demonstrates BOTH tamper-evidence layers catching a forgery:
|
|
10
|
+
// A) LEDGER layer — flip a recorded verdict on disk (unattested -> attested, the
|
|
11
|
+
// canonical "forge a green record" attack) -> verifyTelemetryLedger recomputes
|
|
12
|
+
// every hash independently, so the edited record's hash mismatches AND every
|
|
13
|
+
// record after it breaks the chain (cascade).
|
|
14
|
+
// B) SIGNATURE layer — inflate the reported tokens but keep the original ed25519
|
|
15
|
+
// signature -> verifyTelemetryAttestation rejects it ("signature does not match").
|
|
16
|
+
//
|
|
17
|
+
// No model, no network, no API key, no second repo — runs in a private tmpdir.
|
|
18
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
19
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
20
|
+
};
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.formatTelemetryVerify = formatTelemetryVerify;
|
|
23
|
+
exports.formatTamperDemo = formatTamperDemo;
|
|
24
|
+
exports.runTamperDemo = runTamperDemo;
|
|
25
|
+
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
26
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
27
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
28
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
29
|
+
const telemetry_ledger_1 = require("./telemetry-ledger");
|
|
30
|
+
const telemetry_attestation_1 = require("./telemetry-attestation");
|
|
31
|
+
const execution_backend_1 = require("./execution-backend");
|
|
32
|
+
/** Human-facing render of `telemetry verify <run>`. */
|
|
33
|
+
function formatTelemetryVerify(r) {
|
|
34
|
+
if (!r.present)
|
|
35
|
+
return `telemetry: run ${r.runId} has no attestation ledger (nothing to verify)`;
|
|
36
|
+
const head = r.verified ? `✓ VERIFIED — ${r.records} record(s), chain intact, every hash recomputed independently` : `✗ TAMPERING DETECTED — ${r.failedChecks.length} check(s) failed`;
|
|
37
|
+
const tally = ` attested ${r.attested} · unattested ${r.unattested} · absent ${r.absent}`;
|
|
38
|
+
const fails = r.failedChecks.length ? "\n" + r.failedChecks.map((c) => ` ✗ ${c.name} ${c.code || ""}`).join("\n") : "";
|
|
39
|
+
return `telemetry verify ${r.runId}\n${head}\n${tally}${fails}`;
|
|
40
|
+
}
|
|
41
|
+
/** Human-facing render of `demo tamper` — the visible tamper-evidence proof. */
|
|
42
|
+
function formatTamperDemo(r) {
|
|
43
|
+
const lines = [];
|
|
44
|
+
lines.push(`cw demo tamper — tamper-evidence proof (hermetic, ${r.trustKey} key)`);
|
|
45
|
+
lines.push("");
|
|
46
|
+
lines.push(`▶ Built an attested telemetry ledger: ${r.workers} hops, ${r.baseline.records} records`);
|
|
47
|
+
lines.push(` ${r.baseline.ledgerVerified ? "✓" : "✗"} ledger verifies ${r.baseline.signaturesValid} signed hop(s) verify against the public key`);
|
|
48
|
+
for (const l of r.layers) {
|
|
49
|
+
lines.push("");
|
|
50
|
+
lines.push(`▶ ${l.layer.toUpperCase()} tamper`);
|
|
51
|
+
lines.push(` edit: ${l.tamper}`);
|
|
52
|
+
lines.push(` before: ${l.before.verified ? "✓ verified" : "✗"} — ${l.before.detail}`);
|
|
53
|
+
lines.push(` after: ${l.after.verified ? "✓ (UNDETECTED!)" : "✗ DETECTED"} — ${l.after.detail}`);
|
|
54
|
+
}
|
|
55
|
+
lines.push("");
|
|
56
|
+
lines.push(r.proven
|
|
57
|
+
? "VERDICT: tamper-evidence holds ✓ — every forgery was caught offline, with only the public key. No server was trusted."
|
|
58
|
+
: "VERDICT: PROOF FAILED ✗ — a tamper went undetected. This is a regression in the integrity guarantee.");
|
|
59
|
+
return lines.join("\n");
|
|
60
|
+
}
|
|
61
|
+
// Three hops with a deliberate mix: two signed/attested, one unattested — so the
|
|
62
|
+
// ledger-layer tamper can forge the unattested verdict into "attested" (the exact
|
|
63
|
+
// threat the ledger exists to catch).
|
|
64
|
+
const HOPS = [
|
|
65
|
+
{ workerId: "w-map", taskId: "map:server-api", promptDigest: (0, execution_backend_1.sha256)("map:server-api"), usage: { input_tokens: 2117, output_tokens: 1911 }, attestation: "attested" },
|
|
66
|
+
{ workerId: "w-assess", taskId: "assess:security", promptDigest: (0, execution_backend_1.sha256)("assess:security"), usage: { input_tokens: 1840, output_tokens: 1502 }, attestation: "unattested" },
|
|
67
|
+
{ workerId: "w-verdict", taskId: "verdict:synthesis", promptDigest: (0, execution_backend_1.sha256)("verdict:synthesis"), usage: { input_tokens: 980, output_tokens: 770 }, attestation: "attested" }
|
|
68
|
+
];
|
|
69
|
+
const DEMO_NOW = "2026-01-01T00:00:00.000Z";
|
|
70
|
+
function failingChecks(checks) {
|
|
71
|
+
return checks.filter((c) => !c.pass).map((c) => `${c.name}: ${c.code}`);
|
|
72
|
+
}
|
|
73
|
+
/** Run the full tamper-evidence demonstration in a private tmpdir (cleaned up
|
|
74
|
+
* unless `keepDir` is set). Pure of clock/network; the only nondeterminism is
|
|
75
|
+
* the ephemeral keypair, which never leaves this function. */
|
|
76
|
+
function runTamperDemo(options = {}) {
|
|
77
|
+
const runDir = options.dir || node_fs_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), "cw-tamper-demo-"));
|
|
78
|
+
node_fs_1.default.mkdirSync(runDir, { recursive: true });
|
|
79
|
+
const runId = "demo-tamper-run";
|
|
80
|
+
// Minimal run shape: the ledger API uses only id + paths.runDir.
|
|
81
|
+
const run = { id: runId, paths: { runDir } };
|
|
82
|
+
const { publicKey, privateKey } = node_crypto_1.default.generateKeyPairSync("ed25519");
|
|
83
|
+
const publicKeyPem = publicKey.export({ type: "spki", format: "pem" }).toString();
|
|
84
|
+
const privateKeyPem = privateKey.export({ type: "pkcs8", format: "pem" }).toString();
|
|
85
|
+
// 1. Build a REAL ledger through the production append API, signing each
|
|
86
|
+
// attested hop's usage with the ephemeral key.
|
|
87
|
+
const signed = [];
|
|
88
|
+
for (const hop of HOPS) {
|
|
89
|
+
const ctx = { runId, taskId: hop.taskId, promptDigest: hop.promptDigest };
|
|
90
|
+
const signature = hop.attestation === "attested" ? (0, telemetry_attestation_1.signTelemetry)(hop.usage, privateKeyPem, ctx) : undefined;
|
|
91
|
+
(0, telemetry_ledger_1.appendTelemetryAttestation)(run, {
|
|
92
|
+
workerId: hop.workerId,
|
|
93
|
+
taskId: hop.taskId,
|
|
94
|
+
promptDigest: hop.promptDigest,
|
|
95
|
+
reportedUsage: hop.usage,
|
|
96
|
+
usageSignature: signature,
|
|
97
|
+
attestation: hop.attestation,
|
|
98
|
+
now: DEMO_NOW
|
|
99
|
+
});
|
|
100
|
+
signed.push({ hop, signature });
|
|
101
|
+
}
|
|
102
|
+
// 2. Baseline: the clean ledger verifies, and every signed hop's signature is valid.
|
|
103
|
+
const clean = (0, telemetry_ledger_1.verifyTelemetryLedger)(run);
|
|
104
|
+
const signaturesValid = signed.filter((s) => s.signature && (0, telemetry_attestation_1.verifyTelemetryAttestation)(s.hop.usage, s.signature, publicKeyPem, { runId, taskId: s.hop.taskId, promptDigest: s.hop.promptDigest }).status === "attested").length;
|
|
105
|
+
const baseline = { ledgerVerified: clean.verified, signaturesValid, records: clean.records.length };
|
|
106
|
+
const layers = [];
|
|
107
|
+
// 3a. LEDGER layer — the SOPHISTICATED forgery: flip record[1]'s verdict
|
|
108
|
+
// "unattested" -> "attested" AND recompute its recordHash to cover the edit,
|
|
109
|
+
// so the per-record digest check passes. The chain still catches it: record[2]
|
|
110
|
+
// was linked to the ORIGINAL record[1] hash, so chain-link[2] now breaks. This
|
|
111
|
+
// is the point of the chain over a flat per-record hash — fixing one record's
|
|
112
|
+
// hash cannot be hidden without rewriting every record after it too.
|
|
113
|
+
const ledgerFile = (0, telemetry_ledger_1.telemetryLedgerPath)(run);
|
|
114
|
+
const ledgerJson = JSON.parse(node_fs_1.default.readFileSync(ledgerFile, "utf8"));
|
|
115
|
+
ledgerJson.records[1].attestation = "attested";
|
|
116
|
+
const { recordHash: _stale, ...rest1 } = ledgerJson.records[1];
|
|
117
|
+
ledgerJson.records[1].recordHash = (0, telemetry_ledger_1.computeRecordHash)(rest1); // attacker re-seals the local hash
|
|
118
|
+
node_fs_1.default.writeFileSync(ledgerFile, JSON.stringify(ledgerJson, null, 2));
|
|
119
|
+
const afterLedger = (0, telemetry_ledger_1.verifyTelemetryLedger)(run);
|
|
120
|
+
layers.push({
|
|
121
|
+
layer: "ledger",
|
|
122
|
+
tamper: `forged record[1] verdict "unattested" -> "attested" AND recomputed its recordHash to cover the edit`,
|
|
123
|
+
before: { verified: clean.verified, detail: `${clean.records.length} records: chain intact, all hashes recompute` },
|
|
124
|
+
after: { verified: afterLedger.verified, detail: `the hash chain caught it: ${failingChecks(afterLedger.checks).join(", ")}` },
|
|
125
|
+
failures: failingChecks(afterLedger.checks)
|
|
126
|
+
});
|
|
127
|
+
// 3b. SIGNATURE layer — inflate hop-0's reported output tokens, keep the original
|
|
128
|
+
// signature. The ed25519 verify binds the exact usage bytes, so it rejects.
|
|
129
|
+
const target = signed[0];
|
|
130
|
+
const inflated = { ...target.hop.usage, output_tokens: target.hop.usage.output_tokens * 10 };
|
|
131
|
+
const sigCheck = (0, telemetry_attestation_1.verifyTelemetryAttestation)(inflated, target.signature, publicKeyPem, {
|
|
132
|
+
runId,
|
|
133
|
+
taskId: target.hop.taskId,
|
|
134
|
+
promptDigest: target.hop.promptDigest
|
|
135
|
+
});
|
|
136
|
+
const sigCleanCheck = (0, telemetry_attestation_1.verifyTelemetryAttestation)(target.hop.usage, target.signature, publicKeyPem, {
|
|
137
|
+
runId,
|
|
138
|
+
taskId: target.hop.taskId,
|
|
139
|
+
promptDigest: target.hop.promptDigest
|
|
140
|
+
});
|
|
141
|
+
layers.push({
|
|
142
|
+
layer: "signature",
|
|
143
|
+
tamper: `inflated record[0] reported output_tokens ${target.hop.usage.output_tokens} -> ${inflated.output_tokens}, reused the original ed25519 signature`,
|
|
144
|
+
before: { verified: sigCleanCheck.status === "attested", detail: `signature verifies against the reported usage (${sigCleanCheck.algorithm || "ed25519"})` },
|
|
145
|
+
after: { verified: sigCheck.status === "attested", detail: sigCheck.reason || sigCheck.status },
|
|
146
|
+
failures: sigCheck.status === "attested" ? [] : [`signature: ${sigCheck.reason}`]
|
|
147
|
+
});
|
|
148
|
+
if (!options.keepDir && !options.dir)
|
|
149
|
+
node_fs_1.default.rmSync(runDir, { recursive: true, force: true });
|
|
150
|
+
const proven = baseline.ledgerVerified &&
|
|
151
|
+
baseline.signaturesValid === signed.filter((s) => s.signature).length &&
|
|
152
|
+
layers.every((l) => l.before.verified && !l.after.verified && l.failures.length > 0);
|
|
153
|
+
return { schemaVersion: 1, runId, workers: HOPS.length, trustKey: "ephemeral-ed25519", baseline, layers, proven };
|
|
154
|
+
}
|
package/dist/version.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MIN_SUPPORTED_RUN_STATE_SCHEMA_VERSION = exports.LEGACY_RUN_STATE_SCHEMA_VERSION = exports.CURRENT_RUN_STATE_SCHEMA_VERSION = exports.WORKFLOW_APP_SCHEMA_VERSION = exports.CURRENT_COOL_WORKFLOW_VERSION = void 0;
|
|
4
|
-
exports.CURRENT_COOL_WORKFLOW_VERSION = "0.1.
|
|
4
|
+
exports.CURRENT_COOL_WORKFLOW_VERSION = "0.1.79";
|
|
5
5
|
exports.WORKFLOW_APP_SCHEMA_VERSION = 1;
|
|
6
6
|
exports.CURRENT_RUN_STATE_SCHEMA_VERSION = 1;
|
|
7
7
|
exports.LEGACY_RUN_STATE_SCHEMA_VERSION = 0;
|
package/docs/cli-mcp-parity.7.md
CHANGED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Launch Kit — Cool Workflow
|
|
2
|
+
|
|
3
|
+
Copy for announcing CW. The through-line is the one thing no other agent-pipeline
|
|
4
|
+
tool ships: **you can prove the telemetry, offline, with only a public key.**
|
|
5
|
+
Everything leads with the 30-second `npx cool-workflow demo tamper` proof.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## One-liner
|
|
10
|
+
|
|
11
|
+
> Cool Workflow is an auditable control-plane for multi-agent workflows. It
|
|
12
|
+
> *delegates* model execution — never embeds it — and makes every recorded agent
|
|
13
|
+
> telemetry verdict tamper-evident: anyone can re-verify a run offline with only a
|
|
14
|
+
> public key.
|
|
15
|
+
|
|
16
|
+
## Elevator (2 sentences)
|
|
17
|
+
|
|
18
|
+
> Most agent-pipeline tools log what the model reported and trust it. CW signs and
|
|
19
|
+
> hash-chains every telemetry verdict, so a forged or edited record fails
|
|
20
|
+
> verification — provably, offline — which is what "auditable" has to mean before
|
|
21
|
+
> you let agents touch production work.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Show HN
|
|
26
|
+
|
|
27
|
+
**Title:**
|
|
28
|
+
`Show HN: Cool Workflow – tamper-evident telemetry for agent pipelines (npx demo)`
|
|
29
|
+
|
|
30
|
+
**Body:**
|
|
31
|
+
|
|
32
|
+
> I kept seeing agent-orchestration tools treat the model's self-reported token
|
|
33
|
+
> usage and results as ground truth. For anything auditable that's backwards — a
|
|
34
|
+
> control-plane that trusts unverified self-reports audits *claims*, not facts, and
|
|
35
|
+
> a forged "green" run looks identical to a real one.
|
|
36
|
+
>
|
|
37
|
+
> Cool Workflow is a small, zero-dependency CLI + MCP runtime that takes the
|
|
38
|
+
> opposite stance. It **delegates** model execution to whatever agent you configure
|
|
39
|
+
> (`claude -p`, `codex exec`, an HTTP endpoint) and never embeds a model SDK or
|
|
40
|
+
> holds an API key. What it *does* own is the audit trail: each agent hop's reported
|
|
41
|
+
> usage is signed (ed25519) and appended to a hash-chained ledger, so editing any
|
|
42
|
+
> record — or even recomputing its local hash to cover the edit — breaks the chain
|
|
43
|
+
> downstream. You can re-verify a finished run with only the public key, no network,
|
|
44
|
+
> no trusted server.
|
|
45
|
+
>
|
|
46
|
+
> The 30-second proof, no install:
|
|
47
|
+
>
|
|
48
|
+
> ```
|
|
49
|
+
> npx cool-workflow demo tamper
|
|
50
|
+
> ```
|
|
51
|
+
>
|
|
52
|
+
> It builds a real signed ledger, forges it two ways (flip a verdict + re-seal its
|
|
53
|
+
> hash; inflate reported tokens + reuse the signature), and shows both forgeries
|
|
54
|
+
> caught offline. On a real run, `cw telemetry verify <run>` does the same against
|
|
55
|
+
> what's on disk.
|
|
56
|
+
>
|
|
57
|
+
> Other things it does: concurrent `parallel()` phases with declared collapse
|
|
58
|
+
> semantics (collect-all + kill-on-timeout — 16 agents with a forced hang/crash/
|
|
59
|
+
> dirty-return finish without deadlock and replay "who passed/who failed"), per-task
|
|
60
|
+
> output-schema gates, token budgets enforced against attested usage, and a one-way
|
|
61
|
+
> executor boundary welded into the type system (a callable that could reach a model
|
|
62
|
+
> API fails `npm run build`).
|
|
63
|
+
>
|
|
64
|
+
> Runs anywhere Node runs; `dist/` is committed; BSD-2. It's early (v0.1.79) and I'd
|
|
65
|
+
> genuinely like to hear where the "delegate, prove, replay" model breaks down for
|
|
66
|
+
> your workflows.
|
|
67
|
+
>
|
|
68
|
+
> Repo: https://github.com/coo1white/cool-workflow
|
|
69
|
+
> npm: https://www.npmjs.com/package/cool-workflow
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Short post / tweet thread
|
|
74
|
+
|
|
75
|
+
1/ Your agent pipeline trusts what the model *says* it did. Cool Workflow proves
|
|
76
|
+
it instead. `npx cool-workflow demo tamper` — 30s, no install:
|
|
77
|
+
|
|
78
|
+
2/ It builds a real ed25519-signed telemetry ledger, forges it two ways, and
|
|
79
|
+
catches both offline with only the public key. A control-plane that delegates
|
|
80
|
+
model execution but can still prove the bill is real.
|
|
81
|
+
|
|
82
|
+
3/ Also: concurrent batches that don't deadlock when an agent hangs, schema-gated
|
|
83
|
+
outputs, token budgets vs *attested* usage, and a red line (never call a model
|
|
84
|
+
API) enforced at compile time. Zero deps, BSD-2.
|
|
85
|
+
→ https://github.com/coo1white/cool-workflow
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Why this matters (the wedge, for a longer post)
|
|
90
|
+
|
|
91
|
+
- **Separation of duties.** CW never runs the model, yet can verify the executor's
|
|
92
|
+
reported usage. The thing that *spends the money* is not the thing that *keeps
|
|
93
|
+
the books* — the property auditors require everywhere except, so far, agent
|
|
94
|
+
infra.
|
|
95
|
+
- **Offline, public-key verification.** No telemetry service to trust or breach.
|
|
96
|
+
The record proves its own integrity; the verifier needs only the public key.
|
|
97
|
+
- **Replayable, not just logged.** CW breaks at dispatch and writes to disk, so a
|
|
98
|
+
run replays deterministically — "who passed / who failed" is reconstructable, not
|
|
99
|
+
a scrollback of a fused process.
|
|
100
|
+
- **Fail-closed by default where it counts.** Schema mismatch parks the hop;
|
|
101
|
+
unverifiable usage can be refused (opt-in); an empty-capture result can't be
|
|
102
|
+
presented as a clean commit.
|
|
103
|
+
|
|
104
|
+
## Assets to capture before posting
|
|
105
|
+
|
|
106
|
+
- [ ] A terminal GIF of `npx cool-workflow demo tamper` (the ✗ DETECTED lines are
|
|
107
|
+
the hook) for the README top and the HN/tweet.
|
|
108
|
+
- [ ] Confirm `npx cool-workflow demo tamper` works from a clean machine (no clone).
|
|
109
|
+
- [ ] Pin the npm version badge / release in the first comment.
|
|
110
|
+
|
|
111
|
+
## Channels
|
|
112
|
+
|
|
113
|
+
Hacker News (Show HN), the MCP / agent-tooling communities, r/LocalLLaMA &
|
|
114
|
+
r/MachineLearning (the offline-verification angle), and the npm listing itself
|
|
115
|
+
(keywords already set). Lead every one with the demo command, not the feature
|
|
116
|
+
list.
|
package/docs/project-index.md
CHANGED
|
@@ -5,11 +5,11 @@ Generated from the current repository code on 2026-06-11 by `npm run sync:projec
|
|
|
5
5
|
## Snapshot
|
|
6
6
|
|
|
7
7
|
- Package: `cool-workflow`
|
|
8
|
-
- Version: `0.1.
|
|
9
|
-
- Source modules: `
|
|
8
|
+
- Version: `0.1.79`
|
|
9
|
+
- Source modules: `58`
|
|
10
10
|
- Workflow apps: `6`
|
|
11
11
|
- Docs: `46`
|
|
12
|
-
- Smoke tests: `
|
|
12
|
+
- Smoke tests: `69`
|
|
13
13
|
- Repository: https://github.com/coo1white/cool-workflow
|
|
14
14
|
|
|
15
15
|
## Architecture
|
|
@@ -105,6 +105,7 @@ multi-agent host -> topology -> blackboard/coordinator
|
|
|
105
105
|
- [state-explosion.ts](../src/state-explosion.ts)
|
|
106
106
|
- [state-migrations.ts](../src/state-migrations.ts)
|
|
107
107
|
- [telemetry-attestation.ts](../src/telemetry-attestation.ts)
|
|
108
|
+
- [telemetry-demo.ts](../src/telemetry-demo.ts)
|
|
108
109
|
- [telemetry-ledger.ts](../src/telemetry-ledger.ts)
|
|
109
110
|
- [verifier-registry.ts](../src/verifier-registry.ts)
|
|
110
111
|
- [workbench-host.ts](../src/workbench-host.ts)
|
|
@@ -230,6 +231,7 @@ Smoke tests mirror the public contracts. The high-signal suites are:
|
|
|
230
231
|
- [self-audit-hardening-smoke.js](../test/self-audit-hardening-smoke.js)
|
|
231
232
|
- [state-explosion-management-smoke.js](../test/state-explosion-management-smoke.js)
|
|
232
233
|
- [state-node-smoke.js](../test/state-node-smoke.js)
|
|
234
|
+
- [tamper-evidence-demo-smoke.js](../test/tamper-evidence-demo-smoke.js)
|
|
233
235
|
- [team-collaboration-smoke.js](../test/team-collaboration-smoke.js)
|
|
234
236
|
- [telemetry-attest-wrap-smoke.js](../test/telemetry-attest-wrap-smoke.js)
|
|
235
237
|
- [telemetry-attestation-smoke.js](../test/telemetry-attestation-smoke.js)
|
|
@@ -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.79",
|
|
6
6
|
"license": "BSD-2-Clause",
|
|
7
7
|
"homepage": "https://github.com/coo1white/cool-workflow",
|
|
8
8
|
"author": {
|
package/package.json
CHANGED
|
@@ -65,7 +65,7 @@ const canonicalApps = [
|
|
|
65
65
|
"--source",
|
|
66
66
|
"plugins/cool-workflow/docs/workflow-app-framework.7.md",
|
|
67
67
|
"--scope",
|
|
68
|
-
"Cool Workflow v0.1.
|
|
68
|
+
"Cool Workflow v0.1.79",
|
|
69
69
|
"--freshness",
|
|
70
70
|
"as of release preparation"
|
|
71
71
|
]
|
|
@@ -85,14 +85,14 @@ function main() {
|
|
|
85
85
|
assert.ok(summary, `${app.id} must appear in app list`);
|
|
86
86
|
assert.equal(summary.sourceKind, "app-directory");
|
|
87
87
|
assert.equal(summary.legacy, false);
|
|
88
|
-
assert.equal(summary.version, "0.1.
|
|
88
|
+
assert.equal(summary.version, "0.1.79");
|
|
89
89
|
|
|
90
90
|
const validation = runJson(["app", "validate", manifestPath]);
|
|
91
91
|
assert.equal(validation.valid, true, `${app.id} manifest must validate`);
|
|
92
92
|
|
|
93
93
|
const shown = runJson(["app", "show", app.id]);
|
|
94
94
|
assert.equal(shown.app.id, app.id);
|
|
95
|
-
assert.equal(shown.app.version, "0.1.
|
|
95
|
+
assert.equal(shown.app.version, "0.1.79");
|
|
96
96
|
assert.ok(shown.app.metadata.canonical, `${app.id} must be marked canonical`);
|
|
97
97
|
assert.ok(shown.app.sandboxProfiles.length > 0, `${app.id} must declare sandbox profiles`);
|
|
98
98
|
assertTaskIdsUnique(shown);
|
|
@@ -103,7 +103,7 @@ function main() {
|
|
|
103
103
|
const plan = runJson(["plan", app.id, ...app.args(workspace)]);
|
|
104
104
|
const state = JSON.parse(fs.readFileSync(plan.statePath, "utf8"));
|
|
105
105
|
assert.equal(state.workflow.app.id, app.id);
|
|
106
|
-
assert.equal(state.workflow.app.version, "0.1.
|
|
106
|
+
assert.equal(state.workflow.app.version, "0.1.79");
|
|
107
107
|
assert.equal(state.workflow.app.metadata.canonical, true);
|
|
108
108
|
assert.ok(state.tasks.some((task) => task.requiresEvidence), `${app.id} plan must include evidence gates`);
|
|
109
109
|
assert.ok(state.tasks.every((task) => task.sandboxProfileId), `${app.id} plan must include sandbox hints`);
|
|
@@ -5,7 +5,7 @@ const { spawnSync } = require("node:child_process");
|
|
|
5
5
|
const fs = require("node:fs");
|
|
6
6
|
const path = require("node:path");
|
|
7
7
|
|
|
8
|
-
const TARGET_VERSION = "0.1.
|
|
8
|
+
const TARGET_VERSION = "0.1.79";
|
|
9
9
|
const PREVIOUS_VERSION = "0.1.31";
|
|
10
10
|
const pluginRoot = path.resolve(__dirname, "..");
|
|
11
11
|
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.79");
|
|
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.79 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.79");
|
|
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\.79/);
|
|
199
199
|
assert.match(report, /## Candidates/);
|
|
200
200
|
assert.match(report, /## Trust Audit/);
|
|
201
201
|
assert.match(report, /## Acceptance Rationale/);
|