cool-workflow 0.1.84 → 0.1.86

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 (63) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/README.md +47 -331
  4. package/apps/architecture-review/app.json +1 -1
  5. package/apps/architecture-review-fast/app.json +1 -1
  6. package/apps/architecture-review-fast/workflow.js +2 -0
  7. package/apps/end-to-end-golden-path/app.json +1 -1
  8. package/apps/pr-review-fix-ci/app.json +1 -1
  9. package/apps/release-cut/app.json +1 -1
  10. package/apps/research-synthesis/app.json +1 -1
  11. package/dist/capability-core.js +220 -5
  12. package/dist/capability-registry.js +90 -51
  13. package/dist/cli/command-surface.js +47 -2
  14. package/dist/doctor.js +37 -1
  15. package/dist/mcp/tool-call.js +428 -0
  16. package/dist/mcp/tool-definitions.js +1027 -0
  17. package/dist/mcp-surface.js +5 -1416
  18. package/dist/onramp.js +421 -0
  19. package/dist/orchestrator.js +20 -15
  20. package/dist/run-export.js +139 -1
  21. package/dist/telemetry-demo.js +119 -0
  22. package/dist/types/report-bundle.js +6 -0
  23. package/dist/types.js +1 -0
  24. package/dist/version.js +1 -1
  25. package/dist/workbench-host.js +1 -8
  26. package/docs/agent-delegation-drive.7.md +23 -2
  27. package/docs/cli-mcp-parity.7.md +38 -7
  28. package/docs/contract-migration-tooling.7.md +4 -0
  29. package/docs/control-plane-scheduling.7.md +4 -0
  30. package/docs/dogfood-one-real-repo.7.md +9 -1
  31. package/docs/durable-state-and-locking.7.md +4 -0
  32. package/docs/evidence-adoption-reasoning-chain.7.md +4 -0
  33. package/docs/execution-backends.7.md +4 -0
  34. package/docs/getting-started.md +40 -2
  35. package/docs/index.md +51 -37
  36. package/docs/multi-agent-cli-mcp-surface.7.md +4 -0
  37. package/docs/multi-agent-eval-replay-harness.7.md +4 -0
  38. package/docs/multi-agent-operator-ux.7.md +4 -0
  39. package/docs/node-snapshot-diff-replay.7.md +4 -0
  40. package/docs/observability-cost-accounting.7.md +4 -0
  41. package/docs/project-index.md +23 -7
  42. package/docs/real-execution-backends.7.md +4 -0
  43. package/docs/release-and-migration.7.md +5 -1
  44. package/docs/release-history.md +342 -0
  45. package/docs/release-tooling.7.md +18 -0
  46. package/docs/report-verifiable-bundle.7.md +123 -0
  47. package/docs/run-registry-control-plane.7.md +4 -0
  48. package/docs/run-retention-reclamation.7.md +4 -0
  49. package/docs/state-explosion-management.7.md +4 -0
  50. package/docs/team-collaboration.7.md +4 -0
  51. package/docs/web-desktop-workbench.7.md +4 -0
  52. package/manifest/plugin.manifest.json +1 -1
  53. package/manifest/source-context-profiles.json +1 -1
  54. package/package.json +2 -1
  55. package/scripts/architecture-review-fast.js +44 -2
  56. package/scripts/canonical-apps.js +4 -4
  57. package/scripts/dogfood-release.js +55 -133
  58. package/scripts/golden-path.js +4 -4
  59. package/scripts/onramp-check.js +47 -0
  60. package/scripts/parity-check.js +740 -2
  61. package/scripts/release-check.js +2 -0
  62. package/scripts/source-context.js +31 -5
  63. package/scripts/version-sync-check.js +4 -2
@@ -16,19 +16,26 @@ exports.importRun = importRun;
16
16
  exports.verifyImportedRun = verifyImportedRun;
17
17
  exports.inspectArchive = inspectArchive;
18
18
  exports.importManifestPath = importManifestPath;
19
+ exports.verifyReportBundle = verifyReportBundle;
19
20
  const node_fs_1 = __importDefault(require("node:fs"));
21
+ const node_os_1 = __importDefault(require("node:os"));
20
22
  const node_path_1 = __importDefault(require("node:path"));
21
23
  const node_crypto_1 = __importDefault(require("node:crypto"));
22
24
  const state_1 = require("./state");
23
25
  const version_1 = require("./version");
24
26
  const telemetry_ledger_1 = require("./telemetry-ledger");
27
+ const telemetry_attestation_1 = require("./telemetry-attestation");
25
28
  const trust_audit_1 = require("./trust-audit");
26
29
  const compare_1 = require("./compare");
27
30
  /** Export a run to a portable JSON archive with run-local bytes and digests. */
28
- function exportRun(run, outputPath) {
31
+ function exportRun(run, outputPath, options = {}) {
29
32
  const exportedAt = new Date().toISOString();
30
33
  const files = collectArchiveFiles(run);
31
34
  const manifestSha256 = digestManifest(files);
35
+ // Embed ONLY a public key. resolveTrustPublicKey normalizes an inline PEM or a
36
+ // file path down to inline PEM bytes so the archive is self-contained; a value
37
+ // that resolves to nothing (bad path) yields no trust block rather than a throw.
38
+ const trustPublicKeyPem = (0, telemetry_attestation_1.resolveTrustPublicKey)(options.trustPublicKey);
32
39
  const exported = {
33
40
  schemaVersion: 1,
34
41
  exportedAt,
@@ -39,6 +46,7 @@ function exportRun(run, outputPath) {
39
46
  fileCount: files.length,
40
47
  manifestSha256
41
48
  },
49
+ ...(trustPublicKeyPem ? { trust: { publicKeyPem: trustPublicKeyPem, algorithm: "ed25519" } } : {}),
42
50
  // Legacy field retained so old readers still find an artifact-ish list.
43
51
  artifacts: files
44
52
  .filter((file) => file.role === "artifact")
@@ -62,6 +70,7 @@ function exportRun(run, outputPath) {
62
70
  artifactCount: files.filter((file) => file.role === "artifact").length,
63
71
  auditFileCount: files.filter((file) => file.role === "audit").length,
64
72
  telemetryIncluded: files.some((file) => file.role === "telemetry"),
73
+ trustKeyEmbedded: Boolean(trustPublicKeyPem),
65
74
  manifestSha256,
66
75
  archiveSha256
67
76
  };
@@ -276,6 +285,135 @@ function inspectArchive(archivePath) {
276
285
  function importManifestPath(run) {
277
286
  return node_path_1.default.join(run.paths.runDir, "import-manifest.json");
278
287
  }
288
+ /** Verify a portable run bundle OFFLINE and SELF-CONTAINED: prove the archive bytes,
289
+ * the telemetry hash chain, the trust-audit chain, and (with the bundle's embedded
290
+ * public key) the ed25519 signatures — WITHOUT a source repo, a pre-existing .cw
291
+ * tree, or an out-of-band key. Reuses the existing import + restore-verify path: it
292
+ * restores into an auto-cleaned tmpdir so a stranger's machine is left untouched.
293
+ * Never throws — every failure is a structured check and a false `ok` (exit-1 worthy).
294
+ *
295
+ * Key precedence is bundle > argument > environment, so the artifact verifies the
296
+ * same on any machine; only when the bundle omits a key do the override/env apply. */
297
+ function verifyReportBundle(archivePath, options = {}) {
298
+ const inspect = inspectArchive(archivePath);
299
+ const failedChecks = inspect.checks
300
+ .filter((check) => !check.pass)
301
+ .map((check) => ({ name: check.name, code: check.code }));
302
+ const base = {
303
+ schemaVersion: 1,
304
+ archivePath,
305
+ runId: inspect.runId,
306
+ ok: false,
307
+ archiveOk: inspect.ok,
308
+ telemetryVerified: false,
309
+ trustAuditVerified: false,
310
+ trustKeySource: "none",
311
+ signatureKeyProvided: false,
312
+ signaturesChecked: 0,
313
+ signaturesReverified: 0,
314
+ signaturesFailed: 0,
315
+ failedChecks
316
+ };
317
+ // A bundle that is not even a supported archive cannot be restored — report the
318
+ // inspection failure and stop (fail-closed, no tmpdir, nothing written).
319
+ if (!inspect.schemaSupported)
320
+ return base;
321
+ // Read the embedded trust key (and, if requested, the report bytes) straight from
322
+ // the archive JSON. inspectArchive already proved the bytes parse + digest-match.
323
+ let bundleKey;
324
+ let reportContent;
325
+ try {
326
+ const raw = JSON.parse(node_fs_1.default.readFileSync(archivePath, "utf8"));
327
+ bundleKey = raw.trust?.publicKeyPem;
328
+ if (options.extractReportTo) {
329
+ const reportFile = (raw.files || []).find((file) => file.relativePath === "report.md");
330
+ if (reportFile)
331
+ reportContent = Buffer.from(reportFile.contentBase64, "base64").toString("utf8");
332
+ }
333
+ }
334
+ catch {
335
+ /* inspect already recorded the parse failure; treat key as absent */
336
+ }
337
+ const trustKeySource = bundleKey
338
+ ? "bundle"
339
+ : options.pubkey
340
+ ? "argument"
341
+ : process.env.CW_AGENT_ATTEST_PUBKEY
342
+ ? "environment"
343
+ : "none";
344
+ const trustKey = (0, telemetry_attestation_1.resolveTrustPublicKey)(bundleKey || options.pubkey || process.env.CW_AGENT_ATTEST_PUBKEY);
345
+ const tmpDir = node_fs_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), "cw-verify-bundle-"));
346
+ let telemetryVerified = false;
347
+ let trustAuditVerified = false;
348
+ let signaturesChecked = 0;
349
+ let signaturesReverified = 0;
350
+ let signaturesFailed = 0;
351
+ let reportExtractedTo;
352
+ try {
353
+ // Restore into the throwaway tree. importRun digest-checks every file and throws
354
+ // on the first mismatch (or on a stripped integrity block under
355
+ // CW_REQUIRE_ARCHIVE_INTEGRITY=1) — caught below as a failed "restore" check.
356
+ const imported = importRun(archivePath, tmpDir);
357
+ for (const check of imported.verification.checks) {
358
+ if (check.name === "telemetry-ledger")
359
+ telemetryVerified = check.pass;
360
+ if (check.name === "trust-audit")
361
+ trustAuditVerified = check.pass;
362
+ if (!check.pass)
363
+ failedChecks.push({ name: check.name, code: check.code });
364
+ }
365
+ // Independent ed25519 re-verification over the restored ledger using the bundle's
366
+ // own key (or override/env). With no key, attested records degrade to
367
+ // informational (signaturesFailed stays 0); the chain check above still gates ok.
368
+ const ledger = (0, telemetry_ledger_1.verifyTelemetryLedger)(imported.run);
369
+ const sig = (0, telemetry_attestation_1.verifyTelemetrySignatures)(ledger.records, trustKey);
370
+ signaturesChecked = sig.checked;
371
+ signaturesReverified = sig.reverified;
372
+ signaturesFailed = sig.failed;
373
+ for (const check of sig.checks)
374
+ if (!check.pass)
375
+ failedChecks.push({ name: check.name, code: check.code });
376
+ if (options.extractReportTo && reportContent !== undefined) {
377
+ reportExtractedTo = node_path_1.default.resolve(options.extractReportTo);
378
+ node_fs_1.default.writeFileSync(reportExtractedTo, reportContent);
379
+ }
380
+ }
381
+ catch (error) {
382
+ failedChecks.push({ name: "restore", code: messageOf(error) });
383
+ }
384
+ finally {
385
+ node_fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
386
+ }
387
+ // Strict mode: a bundle that claims attested telemetry but offers no key to check
388
+ // it is unverifiable — refuse rather than green it.
389
+ const strictShortfall = Boolean(options.strictSignatures) && signaturesChecked > 0 && !trustKey;
390
+ if (strictShortfall)
391
+ failedChecks.push({ name: "signatures", code: "signature-key-required" });
392
+ // Extraction was requested but could not be fulfilled (no report.md in the bundle,
393
+ // or the write failed): fail closed rather than silently green a missing artifact —
394
+ // otherwise `report bundle <run> --extract-report r.md && send r.md` would ship
395
+ // nothing (or a stale file) with exit 0. A requested-but-absent output is a failure,
396
+ // not a no-op (distinct from extraction never being requested).
397
+ const extractShortfall = Boolean(options.extractReportTo) && !reportExtractedTo;
398
+ if (extractShortfall)
399
+ failedChecks.push({ name: "extract-report", code: "report-md-unavailable" });
400
+ return {
401
+ schemaVersion: 1,
402
+ archivePath,
403
+ runId: inspect.runId,
404
+ ok: inspect.ok && telemetryVerified && trustAuditVerified && signaturesFailed === 0 && !strictShortfall && !extractShortfall,
405
+ archiveOk: inspect.ok,
406
+ telemetryVerified,
407
+ trustAuditVerified,
408
+ trustKeySource,
409
+ signatureKeyProvided: Boolean(trustKey),
410
+ signaturesChecked,
411
+ signaturesReverified,
412
+ signaturesFailed,
413
+ reportExtractedTo,
414
+ failedChecks
415
+ };
416
+ }
279
417
  function collectArchiveFiles(run) {
280
418
  const entries = new Map();
281
419
  for (const file of walkFiles(run.paths.runDir)) {
@@ -22,12 +22,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
22
22
  exports.formatTelemetryVerify = formatTelemetryVerify;
23
23
  exports.formatTamperDemo = formatTamperDemo;
24
24
  exports.runTamperDemo = runTamperDemo;
25
+ exports.runBundleDemo = runBundleDemo;
26
+ exports.formatBundleDemo = formatBundleDemo;
25
27
  const node_crypto_1 = __importDefault(require("node:crypto"));
26
28
  const node_fs_1 = __importDefault(require("node:fs"));
27
29
  const node_os_1 = __importDefault(require("node:os"));
28
30
  const node_path_1 = __importDefault(require("node:path"));
29
31
  const telemetry_ledger_1 = require("./telemetry-ledger");
30
32
  const telemetry_attestation_1 = require("./telemetry-attestation");
33
+ const run_export_1 = require("./run-export");
34
+ const state_1 = require("./state");
31
35
  const execution_backend_1 = require("./execution-backend");
32
36
  /** Human-facing render of `telemetry verify <run>`. */
33
37
  function formatTelemetryVerify(r) {
@@ -164,3 +168,118 @@ function runTamperDemo(options = {}) {
164
168
  layers.every((l) => l.before.verified && !l.after.verified && l.failures.length > 0);
165
169
  return { schemaVersion: 1, runId, workers: HOPS.length, trustKey: "ephemeral-ed25519", baseline, layers, proven };
166
170
  }
171
+ function runBundleDemo(options = {}) {
172
+ const workdir = options.dir || node_fs_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), "cw-bundle-demo-"));
173
+ node_fs_1.default.mkdirSync(workdir, { recursive: true });
174
+ const runId = "demo-bundle-run";
175
+ const runDir = node_path_1.default.join(workdir, ".cw", "runs", runId);
176
+ const paths = (0, state_1.createRunPaths)(runDir);
177
+ (0, state_1.ensureRunDirs)(paths);
178
+ const { publicKey, privateKey } = node_crypto_1.default.generateKeyPairSync("ed25519");
179
+ const publicKeyPem = publicKey.export({ type: "spki", format: "pem" }).toString();
180
+ const privateKeyPem = privateKey.export({ type: "pkcs8", format: "pem" }).toString();
181
+ // Build a real signed ledger + a cited report, the way an attested run would.
182
+ const ledgerRun = { id: runId, paths };
183
+ for (const hop of HOPS) {
184
+ const ctx = { runId, taskId: hop.taskId, promptDigest: hop.promptDigest };
185
+ (0, telemetry_ledger_1.appendTelemetryAttestation)(ledgerRun, {
186
+ workerId: hop.workerId,
187
+ taskId: hop.taskId,
188
+ promptDigest: hop.promptDigest,
189
+ reportedUsage: hop.usage,
190
+ usageSignature: hop.attestation === "attested" ? (0, telemetry_attestation_1.signTelemetry)(hop.usage, privateKeyPem, ctx) : undefined,
191
+ attestation: hop.attestation,
192
+ now: DEMO_NOW
193
+ });
194
+ }
195
+ node_fs_1.default.writeFileSync(node_path_1.default.join(runDir, "report.md"), "# Architecture review\n\nRisk: src/server.js:18 — unauthenticated route.\n", "utf8");
196
+ const attestedCount = HOPS.filter((h) => h.attestation === "attested").length;
197
+ const fullRun = {
198
+ schemaVersion: 1, id: runId, createdAt: DEMO_NOW, updatedAt: DEMO_NOW, cwd: workdir,
199
+ workflow: { id: "demo", title: "Demo", summary: "", limits: { maxAgents: 1, maxConcurrentAgents: 1 } },
200
+ inputs: { question: "what are the risks?" }, loopStage: "interpret",
201
+ phases: [], tasks: [], dispatches: [], commits: [], paths, nodes: [], contracts: []
202
+ };
203
+ (0, state_1.saveCheckpoint)(fullRun);
204
+ const ledgerFile = (0, telemetry_ledger_1.telemetryLedgerPath)(ledgerRun);
205
+ const cleanLedger = node_fs_1.default.readFileSync(ledgerFile, "utf8");
206
+ const exportSealed = (out) => { (0, run_export_1.exportRun)(fullRun, out, { trustPublicKey: publicKeyPem }); };
207
+ // Baseline: a clean sealed bundle verifies offline; the embedded key reverifies
208
+ // every signed hop.
209
+ const cleanBundle = node_path_1.default.join(workdir, "clean.cwrun.json");
210
+ exportSealed(cleanBundle);
211
+ const clean = (0, run_export_1.verifyReportBundle)(cleanBundle);
212
+ const baseline = { ok: clean.ok, telemetryVerified: clean.telemetryVerified, signaturesReverified: clean.signaturesReverified };
213
+ const layers = [];
214
+ // CHAIN forgery: flip record[1]'s verdict and reseal its recordHash; record[2]'s
215
+ // prevHash still points at the original hash, so the chain breaks — even though
216
+ // every archive file digest (computed at export over the tampered bytes) is valid.
217
+ // This is exactly what inspect-archive alone cannot catch.
218
+ {
219
+ const j = JSON.parse(cleanLedger);
220
+ j.records[1].attestation = "attested";
221
+ const { recordHash: _drop, ...rest } = j.records[1];
222
+ j.records[1].recordHash = (0, telemetry_ledger_1.computeRecordHash)(rest);
223
+ node_fs_1.default.writeFileSync(ledgerFile, JSON.stringify(j, null, 2));
224
+ const forged = node_path_1.default.join(workdir, "forged-chain.cwrun.json");
225
+ exportSealed(forged);
226
+ const after = (0, run_export_1.verifyReportBundle)(forged);
227
+ node_fs_1.default.writeFileSync(ledgerFile, cleanLedger);
228
+ layers.push({
229
+ layer: "chain",
230
+ tamper: `forged record[1] verdict "unattested" -> "attested" and resealed its recordHash; the archive's own file digests stay valid`,
231
+ before: { ok: clean.ok, detail: `${clean.signaturesReverified} signed hop(s) reverify; chain intact` },
232
+ after: { ok: after.ok, detail: after.telemetryVerified ? "telemetry chain still verified (UNDETECTED!)" : "the embedded hash chain broke at the next record" },
233
+ failures: after.ok ? [] : after.failedChecks.map((c) => `${c.name}: ${c.code}`)
234
+ });
235
+ }
236
+ // SIGNATURE forgery: inflate the last attested hop's reported tokens and reseal its
237
+ // usage digest + recordHash so the chain AND archive digests still verify; only the
238
+ // ed25519 signature (over the original usage) no longer matches the inflated number.
239
+ {
240
+ const j = JSON.parse(cleanLedger);
241
+ const idx = j.records.length - 1;
242
+ j.records[idx].reportedUsage = { ...j.records[idx].reportedUsage, output_tokens: j.records[idx].reportedUsage.output_tokens * 10 };
243
+ j.records[idx].reportedUsageDigest = (0, telemetry_ledger_1.reportedUsageDigest)(j.records[idx].reportedUsage);
244
+ const { recordHash: _drop, ...rest } = j.records[idx];
245
+ j.records[idx].recordHash = (0, telemetry_ledger_1.computeRecordHash)(rest);
246
+ node_fs_1.default.writeFileSync(ledgerFile, JSON.stringify(j, null, 2));
247
+ const forged = node_path_1.default.join(workdir, "forged-sig.cwrun.json");
248
+ exportSealed(forged);
249
+ const after = (0, run_export_1.verifyReportBundle)(forged);
250
+ node_fs_1.default.writeFileSync(ledgerFile, cleanLedger);
251
+ layers.push({
252
+ layer: "signature",
253
+ tamper: `inflated the last attested hop's output_tokens 10x and resealed its digest + recordHash; the chain stays valid`,
254
+ before: { ok: clean.ok, detail: `the embedded public key reverifies the original signature` },
255
+ after: { ok: after.ok, detail: after.signaturesFailed > 0 ? `${after.signaturesFailed} signature(s) failed ed25519 reverify` : "signature still verified (UNDETECTED!)" },
256
+ failures: after.ok ? [] : after.failedChecks.map((c) => `${c.name}: ${c.code}`)
257
+ });
258
+ }
259
+ if (!options.keepDir && !options.dir)
260
+ node_fs_1.default.rmSync(workdir, { recursive: true, force: true });
261
+ const proven = baseline.ok &&
262
+ baseline.telemetryVerified &&
263
+ baseline.signaturesReverified === attestedCount &&
264
+ layers.every((l) => l.before.ok && !l.after.ok && l.failures.length > 0);
265
+ return { schemaVersion: 1, runId, workers: HOPS.length, trustKey: "ephemeral-ed25519", baseline, layers, proven };
266
+ }
267
+ function formatBundleDemo(r) {
268
+ const lines = [];
269
+ lines.push(`cw demo bundle — portable-bundle verification proof (hermetic, ${r.trustKey} key)`);
270
+ lines.push("");
271
+ lines.push(`▶ Exported a sealed report bundle: ${r.workers} hops, public key embedded`);
272
+ lines.push(` ${r.baseline.ok ? "✓" : "✗"} bundle verifies offline ${r.baseline.signaturesReverified} signed hop(s) reverify with only the embedded public key`);
273
+ for (const l of r.layers) {
274
+ lines.push("");
275
+ lines.push(`▶ ${l.layer.toUpperCase()} forgery`);
276
+ lines.push(` edit: ${l.tamper}`);
277
+ lines.push(` before: ${l.before.ok ? "✓ verifies" : "✗"} — ${l.before.detail}`);
278
+ lines.push(` after: ${l.after.ok ? "✓ (UNDETECTED!)" : "✗ DETECTED"} — ${l.after.detail}`);
279
+ }
280
+ lines.push("");
281
+ lines.push(r.proven
282
+ ? "VERDICT: bundle verification holds ✓ — every forgery caught offline with only the bundle's embedded public key. No repo, no server, no key handed over."
283
+ : "VERDICT: PROOF FAILED ✗ — a forged bundle verified. This is a regression in the bundle guarantee.");
284
+ return lines.join("\n");
285
+ }
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ // Verifiable report bundle — result shapes shared across the run-export verifier,
3
+ // the capability-core producer (reportBundle), and the quickstart auto-bundle field
4
+ // on QuickstartResult. Kept in types/ (a leaf) so types/drive.ts can reference
5
+ // ReportBundleResult without a types->core import cycle.
6
+ Object.defineProperty(exports, "__esModule", { value: true });
package/dist/types.js CHANGED
@@ -28,6 +28,7 @@ __exportStar(require("./types/sandbox"), exports);
28
28
  __exportStar(require("./types/execution-backend"), exports);
29
29
  __exportStar(require("./types/boundary"), exports);
30
30
  __exportStar(require("./types/drive"), exports);
31
+ __exportStar(require("./types/report-bundle"), exports);
31
32
  __exportStar(require("./types/multi-agent"), exports);
32
33
  __exportStar(require("./types/topology"), exports);
33
34
  __exportStar(require("./types/blackboard"), exports);
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.84";
4
+ exports.CURRENT_COOL_WORKFLOW_VERSION = "0.1.86";
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;
@@ -99,14 +99,7 @@ class WorkbenchHost {
99
99
  }
100
100
  const runMatch = /^\/api\/run\/([^/]+)$/.exec(route);
101
101
  if (runMatch) {
102
- const previousCwd = process.cwd();
103
- process.chdir(this.cwd);
104
- try {
105
- return this.send(res, 200, (0, workbench_1.buildWorkbenchRunView)(this.runner, runMatch[1]));
106
- }
107
- finally {
108
- process.chdir(previousCwd);
109
- }
102
+ return this.send(res, 200, (0, workbench_1.buildWorkbenchRunView)(this.runner.withBaseDir(this.cwd), runMatch[1]));
110
103
  }
111
104
  this.send(res, 404, { error: `no such read-only view: ${route}` });
112
105
  }
@@ -128,6 +128,7 @@ node dist/cli.js backend agent config # show the effective config (se
128
128
  node dist/cli.js backend probe agent --json # ready iff configured, else unverified
129
129
 
130
130
  # drive a real repo end-to-end (zero hand-written result.md)
131
+ node dist/cli.js quickstart architecture-review --check --repo /path/to/repo --question "Is the design sound?" --agent-command "node $(pwd)/scripts/agents/claude-p-agent.js {{input}} {{result}}"
131
132
  node dist/cli.js run architecture-review --drive --repo /path/to/repo --question "Is the design sound?"
132
133
  node dist/cli.js run architecture-review --drive --once --repo /path/to/repo --question "..." # one step
133
134
  node dist/cli.js run drive <run-id> --json # read-only preview of the next step
@@ -135,12 +136,22 @@ node dist/cli.js run drive <run-id> --json # read-only preview of the next
135
136
  # quickstart --resume: a guided stop-then-resume a newcomer can WITNESS in <5 min
136
137
  node dist/cli.js quickstart --resume --repo /path/to/repo --question "..." # advances ONE step, prints a continue line
137
138
  node dist/cli.js quickstart --run <run-id> --resume # continues that run to completion
139
+ node dist/cli.js quickstart --run <run-id> --resume --bundle # continues, then seals a completed run
138
140
  ```
139
141
 
142
+ `quickstart --check` is a zero-write preflight. It does not make a run, write
143
+ `.cw/`, call the agent, write a report, or commit. It checks the app id, repo,
144
+ question, agent config, and (with `--bundle`) the trust-key shape, then gives the
145
+ next command to run. A blocked check exits non-zero, so scripts may use it as a
146
+ gate before a real run.
147
+
140
148
  `quickstart --resume` with no `--run` drives a single step and prints a
141
149
  copy-pasteable `cw quickstart --run <id> --resume` continue line; run it again with
142
150
  the `--run <id>` to finish. The continuing invocation echoes `resumedFrom: <id>`.
143
151
  Bare `quickstart` (no `--resume`) is unchanged — it drives straight to the end.
152
+ When `--bundle` is present on the fresh resume step, no bundle is sealed until
153
+ the run is complete; the continue line keeps `--bundle` so the second command
154
+ finishes and seals the report.
144
155
 
145
156
  For faster first results, use the opt-in fast app in place of changing the full
146
157
  review contract:
@@ -171,9 +182,15 @@ an Assess cache hit. A cache hit still goes through `recordWorkerOutput`
171
182
  validation; a corrupt cached result parks/fails closed rather than spawning a
172
183
  quiet fallback.
173
184
 
185
+ Verify and Verdict also get the source-context instruction so they do not have to
186
+ start by scanning the repo again. They are not result-cached; they still have to
187
+ cite evidence and make the final check from the accepted Map and Assess work.
188
+
174
189
  `--metrics` is diagnostic and opt-in. It adds elapsed milliseconds, step counts,
175
- agent-spawn counts, and `result-cache` hit counts to the wrapper JSON payload;
176
- without it, the wrapper's default output shape stays unchanged.
190
+ agent-spawn counts, `result-cache` hit counts, source-context bytes/digest, and
191
+ one row per driven task with phase, task id, elapsed time, and spawn/cache state
192
+ to the wrapper JSON payload; without it, the wrapper's default output shape stays
193
+ unchanged.
177
194
 
178
195
  `{{manifest}}`, `{{input}}`, `{{result}}`, `{{workerDir}}`, `{{model}}`, and
179
196
  `{{prompt}}` are put into DISCRETE argv elements (never a shell-interpreted
@@ -270,3 +287,7 @@ Loaders fail closed on corrupt state; store writes are made safe under more than
270
287
  ## Privacy Release (v0.1.84)
271
288
 
272
289
  No other change to this page in v0.1.84.
290
+
291
+ 0.1.85
292
+
293
+ 0.1.86
@@ -64,6 +64,16 @@ ISO timestamps from the moment of generation. The `--json` payload is the
64
64
  contract, and it is the same bytes the MCP tool gives back. The human text view
65
65
  is policy put on top; it never changes the payload.
66
66
 
67
+ Some payload checks need more than `cwd` or `runId`. The registry may name a
68
+ scenario probe for a safe local case. A scenario gets two new temp workspaces,
69
+ one for CLI and one for MCP, sets up the same state, runs one capability through
70
+ each door, and then compares the payload after taking out temp roots, run ids,
71
+ time stamps, and chain hashes that are made by that workspace. This is used for
72
+ local deterministic work such as app show/validate/package, topology
73
+ show/validate/apply/summary/graph, sandbox show/validate/choose/resolve, state
74
+ summary refresh/show, `plan`, `approve`, `reject`, `comment.add`, `handoff`, and
75
+ `review.policy`.
76
+
67
77
  ## The Parity Matrix
68
78
 
69
79
  The matrix below is made from the live registry — one row per capability,
@@ -72,7 +82,7 @@ relationship. `identical` means `cw <cmd> --json` is equal to the `cw_<tool>`
72
82
  payload; `projected` means a declared divergence with a reason; `cli-only` marks
73
83
  a surface-specific capability with a recorded reason. The matrix is
74
84
  <!-- gen:parity:count -->
75
- machine-complete by design: 190 capabilities, 184 MCP tools.
85
+ machine-complete by design: 193 capabilities, 186 MCP tools.
76
86
  <!-- /gen:parity:count -->
77
87
 
78
88
  <!-- gen:parity:table -->
@@ -236,6 +246,8 @@ machine-complete by design: 190 capabilities, 184 MCP tools.
236
246
  | `run.import` | `cw run import` | `cw_run_import` | `runImportArchive` | both | identical |
237
247
  | `run.verify-import` | `cw run verify-import` | `cw_run_verify_import` | `runVerifyImport` | both | identical |
238
248
  | `run.inspect-archive` | `cw run inspect-archive` | `cw_run_inspect_archive` | `runInspectArchive` | both | identical |
249
+ | `report.verify-bundle` | `cw report verify-bundle` | `cw_report_verify_bundle` | `runVerifyReportBundle` | both | identical |
250
+ | `report.bundle` | `cw report bundle` | `cw_report_bundle` | `reportBundle` | both | identical |
239
251
  | `run.drive` | `cw run drive` | `cw_run_drive` | `runDrivePreview` | both | identical |
240
252
  | `run.drive.step` | `cw run drive` | `cw_run_drive_step` | `runDrive` | both | projected |
241
253
  | `quickstart` | `cw quickstart` | `—` | `quickstart` | cli-only | cli-only |
@@ -256,6 +268,7 @@ machine-complete by design: 190 capabilities, 184 MCP tools.
256
268
  | `gc.verify` | `cw gc verify` | `cw_gc_verify` | `gcVerify` | both | identical |
257
269
  | `telemetry.verify` | `cw telemetry verify` | `cw_telemetry_verify` | `telemetryVerify` | both | identical |
258
270
  | `demo.tamper` | `cw demo tamper` | `—` | `demoTamper` | cli-only | cli-only |
271
+ | `demo.bundle` | `cw demo bundle` | `—` | `demoBundle` | cli-only | cli-only |
259
272
  | `history` | `cw history` | `cw_history` | `runRegistry.history` | both | identical |
260
273
  | `workbench.view` | `cw workbench view` | `cw_workbench_view` | `buildWorkbenchRunView` | both | identical |
261
274
  | `workbench.serve` | `cw workbench serve` | `cw_workbench_serve` | `buildWorkbenchServeDescriptor` | both | projected |
@@ -282,14 +295,15 @@ A capability may be on one surface only, but never without word of it — it mus
282
295
  carry a recorded reason in the registry.
283
296
 
284
297
  <!-- gen:parity:cliOnly -->
285
- Six capabilities are CLI-only:
298
+ Seven capabilities are CLI-only:
286
299
 
287
300
  - `help` — Human help text. MCP hosts enumerate capabilities via tools/list, not a help command.
288
301
  - `doctor` — Environment diagnostics are inherently local to the CLI host — Node version, $PATH, $CW_HOME/cwd writability. An MCP client diagnosing the server process's environment is not meaningful; agents already receive the same readiness facts in their typed results (e.g. status: blocked, agentConfigured). Inspired by `brew doctor`.
289
302
  - `loop` — Convenience alias of `schedule create` with kind=loop. MCP hosts use cw_schedule_create with kind=loop.
290
303
  - `schedule daemon` — Long-running desktop daemon process, not a request/response tool. MCP hosts drive ticks via cw_schedule_due + cw_schedule_run_now.
291
- - `quickstart` — CLI UX convenience layer (newcomer first value in one command) over the existing run.drive.step + report verbs; it spawns nothing new and delegates worker execution to the operator's agent backend. MCP hosts compose the same outcome from cw_run_drive_step + cw_report. `audit-run` is a CLI-only alias of the same wrapper.
304
+ - `quickstart` — CLI UX convenience layer (newcomer first value in one command) over the existing run.drive.step + report verbs; it spawns nothing new and delegates worker execution to the operator's agent backend. MCP hosts compose the same outcome from cw_run_drive_step + cw_report (+ cw_report_bundle for --bundle). `audit-run` is a CLI-only alias of the same wrapper.
292
305
  - `demo tamper` — 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.
306
+ - `demo bundle` — Human-facing demonstration (operator/newcomer onboarding); the underlying integrity check is exposed programmatically as the both-surface report.verify-bundle. No agent or MCP client needs to invoke a demo.
293
307
  <!-- /gen:parity:cliOnly -->
294
308
 
295
309
  <!-- gen:parity:projected -->
@@ -328,10 +342,23 @@ of the rules above.
328
342
  registry ⇄ CLI ⇄ MCP coverage (every declared capability is found on its declared
329
343
  surfaces and nothing live is undeclared), makes sure `--json` output is equal to
330
344
  the MCP payload for every `payloadIdentical` capability, makes sure of the
331
- declared `commit` projection, and makes sure of fail-closed behavior by putting in
332
- drift a peer taken away, an undeclared tool, an exception with no reason, a
333
- changed payload and checking that the gate says no to each one. It is part of
334
- `npm test` and `npm run release:check`.
345
+ declared `commit` projection, checks that safe write/multi-argument capabilities
346
+ are named scenario probes rather than deferred work, and makes sure of
347
+ fail-closed behavior by putting in drift a peer taken away, an undeclared tool,
348
+ an exception with no reason, a bad probe classification, a changed payload — and
349
+ checking that the gate says no to each one. It is part of `npm test` and
350
+ `npm run release:check`.
351
+
352
+ The scenario probes now cover local worker/candidate/feedback read and write
353
+ paths. They make temp runs, send out a worker, write a fixed `result.md`, score
354
+ and pick a candidate, and make feedback from local state. No outside agent is
355
+ run; the probe checks only that CLI and MCP carry the same JSON to the same CW
356
+ core.
357
+
358
+ They also cover local state-node read, snapshot, diff, replay, and replay-check
359
+ paths from the same temp run. Snapshot and replay ids are made by CW, so the
360
+ parity check sets aside only those made ids and timestamps before it compares the
361
+ JSON.
335
362
 
336
363
  In CW, parity is not a custom; it is a built, declared, and kept property of the
337
364
  build. It is not done till it is put in the docs and tested.
@@ -466,3 +493,7 @@ Loaders fail closed on corrupt state; store writes are made safe under more than
466
493
  ## Privacy Release (v0.1.84)
467
494
 
468
495
  No other change to this page in v0.1.84.
496
+
497
+ 0.1.85
498
+
499
+ 0.1.86
@@ -141,3 +141,7 @@ Loaders fail closed on corrupt state; store writes are made safe under more than
141
141
  ## Privacy Release (v0.1.84)
142
142
 
143
143
  No other change to this page in v0.1.84.
144
+
145
+ 0.1.85
146
+
147
+ 0.1.86
@@ -125,3 +125,7 @@ Loaders fail closed on corrupt state; store writes are made safe under more than
125
125
  ## Privacy Release (v0.1.84)
126
126
 
127
127
  No other change to this page in v0.1.84.
128
+
129
+ 0.1.85
130
+
131
+ 0.1.86
@@ -89,12 +89,20 @@ report, and audit tools.
89
89
 
90
90
  ```bash
91
91
  node test/dogfood-release-smoke.js
92
+ node test/dogfood-architecture-review-smoke.js
92
93
  ```
93
94
 
94
95
  The smoke test runs `scripts/dogfood-release.js --smoke --json`. It still
95
96
  uses the real repository, `release-cut`, worker manifests, trust audit records,
96
97
  candidate scoring, selection, verifier-gated commit, and a report, but keeps the
97
- command set smaller so it does not do recursive release checking.
98
+ command set smaller so it does not do recursive release checking. Smoke mode
99
+ does not run `canonical-apps` or `golden-path`; `npm test` runs those checks as
100
+ separate smokes.
101
+
102
+ The architecture-review smoke runs beside it. That smoke uses a stub agent to
103
+ drive the real `architecture-review` app to a report and audit proof. Keeping it
104
+ as a second smoke lets the parallel gate run both dogfood halves at the same
105
+ time while keeping the same proof.
98
106
 
99
107
  ## Promote To Real Release Actions
100
108
 
@@ -124,3 +124,7 @@ Loaders fail closed on corrupt state; store writes are made safe under more than
124
124
  ## Privacy Release (v0.1.84)
125
125
 
126
126
  No other change to this page in v0.1.84.
127
+
128
+ 0.1.85
129
+
130
+ 0.1.86
@@ -285,3 +285,7 @@ Loaders fail closed on corrupt state; store writes are made safe under more than
285
285
  ## Privacy Release (v0.1.84)
286
286
 
287
287
  No other change to this page in v0.1.84.
288
+
289
+ 0.1.85
290
+
291
+ 0.1.86
@@ -315,3 +315,7 @@ Loaders fail closed on corrupt state; store writes are made safe under more than
315
315
  ## Privacy Release (v0.1.84)
316
316
 
317
317
  No other change to this page in v0.1.84.
318
+
319
+ 0.1.85
320
+
321
+ 0.1.86
@@ -17,6 +17,8 @@ start a run:
17
17
  ```bash
18
18
  node scripts/cw.js doctor # human-readable
19
19
  node scripts/cw.js doctor --json # stable payload for scripts
20
+ node scripts/cw.js doctor --onramp # short path for users and code work
21
+ node scripts/cw.js doctor --onramp --changed-from origin/main
20
22
  ```
21
23
 
22
24
  It checks the Node version (v18+), whether an agent backend is set up (and its
@@ -26,6 +28,22 @@ read-only — it makes nothing on disk. It exits non-zero only on a blocking
26
28
  problem; a missing agent is a warning (you are still able to run `demo` and
27
29
  `--preview`).
28
30
 
31
+ Use `--onramp` when you are not certain what to do next. It keeps the main path
32
+ small:
33
+
34
+ 1. `cw demo tamper` - prove the trust check with no agent.
35
+ 2. `cw quickstart architecture-review --check ...` - check a real run with no
36
+ writes.
37
+ 3. `cw quickstart architecture-review ...` - make the report.
38
+ 4. `cw quickstart architecture-review ... --bundle` - make a portable report
39
+ file for another person.
40
+ 5. `cw report verify-bundle report.cwrun.json` - check that file offline.
41
+ 6. `npm run test:fast` - use the fast code check while you work.
42
+ 7. `npm run release:check` - use the full gate only when the batch is ready.
43
+
44
+ Add `--changed-from origin/main` in a source checkout to get the nearest smoke
45
+ tests and guard checks for your current change.
46
+
29
47
  Make a run with a canonical workflow app:
30
48
 
31
49
  ```bash
@@ -73,17 +91,37 @@ node scripts/cw.js eval report .cw/evals/<suite-id>/replay-run.json
73
91
  node scripts/cw.js report <run-id> --show
74
92
  ```
75
93
 
76
- Run the deterministic regression commands. They give the same result every time:
94
+ Run the smallest check that fits the change:
77
95
 
78
96
  ```bash
79
97
  npm run check
80
- npm test
98
+ npm run build
99
+ node test/<nearest-smoke>.js
100
+ npm run onramp:check
101
+ npm run test:fast
102
+ npm test # slow serial backstop
81
103
  npm run canonical-apps
82
104
  npm run golden-path
83
105
  npm run eval:replay
84
106
  npm run fixture-compat
85
107
  ```
86
108
 
109
+ When a test run is slow, make a read-only timing report:
110
+
111
+ ```bash
112
+ npm run test:ci -- --json-summary /tmp/cw-test-summary.json
113
+ ```
114
+
115
+ Use the `slowest` list in that file to choose one test-speed cycle. This is a
116
+ guide, not a release gate.
117
+
118
+ For a CLI or MCP surface change, also run:
119
+
120
+ ```bash
121
+ npm run parity:check
122
+ npm run gen:manifests -- --check
123
+ ```
124
+
87
125
  Before you cut a release, run the full dry-run gate:
88
126
 
89
127
  ```bash