cool-workflow 0.2.2 → 0.2.3

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 (136) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/apps/architecture-review/app.json +1 -1
  4. package/apps/architecture-review-fast/app.json +1 -1
  5. package/apps/end-to-end-golden-path/app.json +1 -1
  6. package/apps/pr-review-fix-ci/app.json +1 -1
  7. package/apps/release-cut/app.json +1 -1
  8. package/apps/research-synthesis/app.json +1 -1
  9. package/dist/cli/dispatch.js +11 -5
  10. package/dist/cli/entry.js +41 -1
  11. package/dist/cli/io.js +6 -2
  12. package/dist/cli/parseargv.js +1 -0
  13. package/dist/core/capability-data.js +1 -0
  14. package/dist/core/format/completion.js +68 -0
  15. package/dist/core/format/help.js +25 -6
  16. package/dist/core/format/safe-json.js +73 -0
  17. package/dist/core/format/state-explosion-text.js +1 -1
  18. package/dist/core/multi-agent/candidate-scoring.js +5 -1
  19. package/dist/core/multi-agent/collaboration.js +3 -3
  20. package/dist/core/multi-agent/coordinator.js +5 -5
  21. package/dist/core/multi-agent/runtime.js +4 -4
  22. package/dist/core/multi-agent/topology.js +3 -3
  23. package/dist/core/pipeline/dispatch.js +18 -4
  24. package/dist/core/pipeline/drive-decide.js +2 -1
  25. package/dist/core/state/migrations.js +16 -1
  26. package/dist/core/state/state-explosion/digest.js +13 -13
  27. package/dist/core/state/state-explosion/graph.js +17 -11
  28. package/dist/core/state/state-explosion/report.js +6 -6
  29. package/dist/core/util/numeric-flag.js +40 -0
  30. package/dist/core/version.js +1 -1
  31. package/dist/mcp/server.js +99 -11
  32. package/dist/shell/audit-cli.js +57 -25
  33. package/dist/shell/coordinator-io.js +73 -13
  34. package/dist/shell/dispatch.js +1 -1
  35. package/dist/shell/doctor.js +80 -1
  36. package/dist/shell/drive.js +243 -49
  37. package/dist/shell/eval-text.js +2 -2
  38. package/dist/shell/evidence-reasoning.js +4 -4
  39. package/dist/shell/execution-backend/agent.js +20 -1
  40. package/dist/shell/execution-backend/container.js +4 -1
  41. package/dist/shell/execution-backend/local.js +4 -1
  42. package/dist/shell/feedback-cli.js +6 -6
  43. package/dist/shell/fs-atomic.js +218 -29
  44. package/dist/shell/man-cli.js +6 -0
  45. package/dist/shell/metrics-cli.js +2 -1
  46. package/dist/shell/multi-agent-cli.js +367 -323
  47. package/dist/shell/multi-agent-host.js +9 -9
  48. package/dist/shell/multi-agent-operator-ux.js +80 -38
  49. package/dist/shell/node-store.js +10 -4
  50. package/dist/shell/observability.js +1 -1
  51. package/dist/shell/operator-ux-text.js +22 -22
  52. package/dist/shell/operator-ux.js +15 -14
  53. package/dist/shell/orchestrator.js +49 -38
  54. package/dist/shell/pipeline-cli.js +87 -40
  55. package/dist/shell/reclamation-io.js +14 -4
  56. package/dist/shell/registry-cli.js +19 -17
  57. package/dist/shell/remote-source.js +13 -8
  58. package/dist/shell/report-cli.js +45 -0
  59. package/dist/shell/report.js +2 -1
  60. package/dist/shell/run-registry-io.js +77 -19
  61. package/dist/shell/run-store.js +74 -2
  62. package/dist/shell/scheduling-io.js +12 -13
  63. package/dist/shell/state-cli.js +2 -7
  64. package/dist/shell/state-explosion-cli.js +17 -9
  65. package/dist/shell/topology-io.js +36 -5
  66. package/dist/shell/trust-audit.js +224 -22
  67. package/dist/shell/trust-policy-io.js +1 -1
  68. package/dist/shell/worker-cli.js +35 -31
  69. package/dist/wiring/capability-table/basics.js +33 -8
  70. package/dist/wiring/capability-table/exec-backend.js +20 -11
  71. package/dist/wiring/capability-table/multi-agent.js +172 -165
  72. package/dist/wiring/capability-table/parity.js +1 -0
  73. package/dist/wiring/capability-table/pipeline.js +54 -42
  74. package/dist/wiring/capability-table/registry-core.js +29 -10
  75. package/dist/wiring/capability-table/reporting.js +137 -96
  76. package/dist/wiring/capability-table/scheduling-registry.js +195 -161
  77. package/dist/wiring/capability-table/state.js +36 -28
  78. package/dist/wiring/capability-table/trust-ledger.js +46 -22
  79. package/dist/wiring/capability-table/workflow-apps.js +83 -68
  80. package/docs/agent-delegation-drive.7.md +2 -0
  81. package/docs/candidate-scoring.7.md +1 -1
  82. package/docs/canonical-workflow-apps.7.md +7 -7
  83. package/docs/cli-mcp-parity.7.md +8 -3
  84. package/docs/contract-migration-tooling.7.md +2 -0
  85. package/docs/control-plane-scheduling.7.md +2 -0
  86. package/docs/coordinator-blackboard.7.md +17 -17
  87. package/docs/dogfood-one-real-repo.7.md +11 -11
  88. package/docs/durable-state-and-locking.7.md +2 -0
  89. package/docs/end-to-end-golden-path.7.md +14 -14
  90. package/docs/evidence-adoption-reasoning-chain.7.md +2 -0
  91. package/docs/execution-backends.7.md +2 -0
  92. package/docs/getting-started.md +37 -37
  93. package/docs/multi-agent-cli-mcp-surface.7.md +19 -17
  94. package/docs/multi-agent-eval-replay-harness.7.md +15 -13
  95. package/docs/multi-agent-operator-ux.7.md +21 -19
  96. package/docs/multi-agent-runtime-core.7.md +22 -22
  97. package/docs/multi-agent-topologies.7.md +6 -6
  98. package/docs/multi-agent-trust-policy-audit.7.md +11 -11
  99. package/docs/node-snapshot-diff-replay.7.md +2 -0
  100. package/docs/observability-cost-accounting.7.md +2 -0
  101. package/docs/operator-ux.7.md +34 -34
  102. package/docs/pipeline-runner.7.md +4 -4
  103. package/docs/project-index.md +27 -4
  104. package/docs/real-execution-backends.7.md +2 -0
  105. package/docs/release-and-migration.7.md +3 -1
  106. package/docs/release-tooling.7.md +2 -0
  107. package/docs/routines.md +4 -4
  108. package/docs/run-registry-control-plane.7.md +21 -19
  109. package/docs/run-retention-reclamation.7.md +2 -0
  110. package/docs/scheduled-tasks.md +14 -14
  111. package/docs/security-trust-hardening.7.md +15 -15
  112. package/docs/state-explosion-management.7.md +12 -10
  113. package/docs/team-collaboration.7.md +2 -0
  114. package/docs/trust-audit-anchor.7.md +2 -0
  115. package/docs/unix-principles.md +3 -1
  116. package/docs/verifier-gated-commit.7.md +1 -1
  117. package/docs/web-desktop-workbench.7.md +2 -0
  118. package/docs/workflow-app-framework.7.md +13 -13
  119. package/manifest/plugin.manifest.json +1 -1
  120. package/package.json +2 -2
  121. package/scripts/agents/claude-p-agent.js +2 -2
  122. package/scripts/block-unapproved-tag.sh +23 -2
  123. package/scripts/canonical-apps.js +4 -4
  124. package/scripts/children/batch-delegate-child.js +52 -2
  125. package/scripts/dogfood-release.js +1 -1
  126. package/scripts/fake-date-for-reproduction.js +44 -0
  127. package/scripts/golden-path.js +4 -4
  128. package/scripts/purity-baseline.json +3 -0
  129. package/scripts/release-flow.js +57 -1
  130. package/scripts/verdict-keygen.js +83 -0
  131. package/scripts/verify-bump-reproduction.sh +148 -0
  132. package/scripts/verify-verdict-signature.js +61 -0
  133. package/skills/cool-workflow/SKILL.md +9 -9
  134. package/skills/cool-workflow/references/commands.md +89 -88
  135. package/ui/workbench/app.css +37 -1
  136. package/ui/workbench/app.js +124 -6
@@ -113,6 +113,7 @@ exports.evalScoreCli = evalScoreCli;
113
113
  exports.evalGateCli = evalGateCli;
114
114
  exports.evalReportCli = evalReportCli;
115
115
  const path = __importStar(require("node:path"));
116
+ const numeric_flag_1 = require("../core/util/numeric-flag");
116
117
  const run_store_1 = require("./run-store");
117
118
  const report_1 = require("./report");
118
119
  const mai = __importStar(require("./multi-agent-io"));
@@ -140,6 +141,18 @@ function invocationCwd(args) {
140
141
  function loadRun(args, runId) {
141
142
  return (0, run_store_1.loadRunFromCwd)(runId, invocationCwd(args));
142
143
  }
144
+ /** Hold the run's state.json lock across the WHOLE load -> change ->
145
+ * persist cycle. A bare loadRun + persist pair races: two processes both
146
+ * load the same state and the later persist silently drops the earlier
147
+ * change (reproduced — N concurrent `blackboard topic create` on one run
148
+ * kept only 1). withRunStateLock loads the run UNDER the lock, and the
149
+ * saveCheckpoint inside persist re-enters that same lock (withFileLock is
150
+ * in-process re-entrant), exactly as pipeline-cli.ts's dispatchRun /
151
+ * recordResultRun already do. Every persist()-calling verb in this file
152
+ * runs through here; read-only verbs keep the lock-free loadRun. */
153
+ function mutateRun(args, runId, fn) {
154
+ return (0, run_store_1.withRunStateLock)(runId, invocationCwd(args), fn);
155
+ }
143
156
  function persist(run) {
144
157
  (0, run_store_1.saveCheckpoint)(run);
145
158
  (0, report_1.writeReport)(run);
@@ -294,27 +307,28 @@ function topologyValidateCli(topologyId) {
294
307
  function topologyApplyCli(args) {
295
308
  const runId = requireArg(args.runId, "run id");
296
309
  const topologyId = requireArg(args.topologyId ?? args.id, "topology id");
297
- const run = loadRun(args, runId);
298
- const record = topio.applyTopology(run, topologyId, {
299
- // #30: `topology apply <run> <topology> --id <custom>` — the custom
300
- // topology-run id arrives as `--id` (parseArgv key `id`), NOT `id2`
301
- // (which no CLI/MCP surface ever emits). On CLI the topology id comes
302
- // from positional[1] (capability-table), so `args.id` is free for the
303
- // run-id override; on MCP the handler pre-maps topologyId from
304
- // `topologyId ?? id`. Byte-exact to the old build's
305
- // `id: stringOption(options.id)` (orchestrator/topology-operations.ts).
306
- id: optionalString(args.id),
307
- task: undefined,
308
- taskIds: arrayArg(args.task ?? args.taskId),
309
- mapperCount: numberArg(args.mapperCount ?? args["mapper-count"] ?? args.mappers ?? args.mapper),
310
- judgeCount: numberArg(args.judgeCount ?? args["judge-count"] ?? args.judges ?? args.judge),
311
- debateRounds: numberArg(args.debateRounds ?? args["debate-rounds"] ?? args.rounds),
312
- blackboardId: blackboardIdArg(args),
313
- multiAgentRunId: multiAgentRunArg(args),
314
- collectInitialFanin: boolArg(args.collectInitialFanin ?? args["collect-initial-fanin"]),
310
+ return mutateRun(args, runId, (run) => {
311
+ const record = topio.applyTopology(run, topologyId, {
312
+ // #30: `topology apply <run> <topology> --id <custom>` — the custom
313
+ // topology-run id arrives as `--id` (parseArgv key `id`), NOT `id2`
314
+ // (which no CLI/MCP surface ever emits). On CLI the topology id comes
315
+ // from positional[1] (capability-table), so `args.id` is free for the
316
+ // run-id override; on MCP the handler pre-maps topologyId from
317
+ // `topologyId ?? id`. Byte-exact to the old build's
318
+ // `id: stringOption(options.id)` (orchestrator/topology-operations.ts).
319
+ id: optionalString(args.id),
320
+ task: undefined,
321
+ taskIds: arrayArg(args.task ?? args.taskId),
322
+ mapperCount: numberArg(args.mapperCount ?? args["mapper-count"] ?? args.mappers ?? args.mapper),
323
+ judgeCount: numberArg(args.judgeCount ?? args["judge-count"] ?? args.judges ?? args.judge),
324
+ debateRounds: numberArg(args.debateRounds ?? args["debate-rounds"] ?? args.rounds),
325
+ blackboardId: blackboardIdArg(args),
326
+ multiAgentRunId: multiAgentRunArg(args),
327
+ collectInitialFanin: boolArg(args.collectInitialFanin ?? args["collect-initial-fanin"]),
328
+ });
329
+ persist(run);
330
+ return record;
315
331
  });
316
- persist(run);
317
- return record;
318
332
  }
319
333
  function topologySummaryCli(args) {
320
334
  const runId = requireArg(args.runId, "run id");
@@ -338,59 +352,60 @@ function topologyRunShowCli(args, topologyRunId) {
338
352
  // ---------------------------------------------------------------------------
339
353
  function multiAgentRunCli(args) {
340
354
  const runId = requireArg(args.runId, "run id");
341
- const run = loadRun(args, runId);
342
- const topology = optionalString(args.topology);
343
- // `multi-agent run <run> <id> --status <status>` — port of the old CLI
344
- // handler's `id && args.options.status` arm (cli/handlers/multi-agent.ts):
345
- // transition the MultiAgentRun lifecycle (the core cascades completion to
346
- // owned roles/groups/fanouts/fanins, or fails closed when a fanin is not
347
- // verifier-ready). `multiAgentRunId` is the positional entity id the CLI
348
- // binding forwards (or the `--id`/`--multi-agent-run` alias the MCP peer
349
- // sends).
350
- const transitionId = optionalString(args.multiAgentRunId ?? args.id ?? args.multiAgentRun);
351
- const status = optionalString(args.status);
352
- const app = optionalString(args.app ?? args.appId);
353
- const workflow = optionalString(args.workflow ?? args.workflowId);
354
- const isHostRun = !runId || topology !== undefined || app !== undefined || workflow !== undefined;
355
- if (!isHostRun && transitionId && status && args.title === undefined && args.objective === undefined) {
356
- const record = mai.transitionMultiAgentRun(run, transitionId, status, {
357
- reason: optionalString(args.reason),
358
- actor: optionalString(args.actor),
359
- });
355
+ return mutateRun(args, runId, (run) => {
356
+ const topology = optionalString(args.topology);
357
+ // `multi-agent run <run> <id> --status <status>` — port of the old CLI
358
+ // handler's `id && args.options.status` arm (cli/handlers/multi-agent.ts):
359
+ // transition the MultiAgentRun lifecycle (the core cascades completion to
360
+ // owned roles/groups/fanouts/fanins, or fails closed when a fanin is not
361
+ // verifier-ready). `multiAgentRunId` is the positional entity id the CLI
362
+ // binding forwards (or the `--id`/`--multi-agent-run` alias the MCP peer
363
+ // sends).
364
+ const transitionId = optionalString(args.multiAgentRunId ?? args.id ?? args.multiAgentRun);
365
+ const status = optionalString(args.status);
366
+ const app = optionalString(args.app ?? args.appId);
367
+ const workflow = optionalString(args.workflow ?? args.workflowId);
368
+ const isHostRun = !runId || topology !== undefined || app !== undefined || workflow !== undefined;
369
+ if (!isHostRun && transitionId && status && args.title === undefined && args.objective === undefined) {
370
+ const record = mai.transitionMultiAgentRun(run, transitionId, status, {
371
+ reason: optionalString(args.reason),
372
+ actor: optionalString(args.actor),
373
+ });
374
+ persist(run);
375
+ return record;
376
+ }
377
+ if (!isHostRun && transitionId && !status && args.title === undefined && args.objective === undefined && args.id === undefined) {
378
+ // `multi-agent run <run> <id>` (positional id, no status/create flags) —
379
+ // SHOW the MultiAgentRun record (old handler's showMultiAgentRun arm).
380
+ const record = (0, runtime_1.getMultiAgentRun)(run, transitionId);
381
+ if (!record)
382
+ throw new Error(`Unknown MultiAgentRun id: ${transitionId}`);
383
+ return record;
384
+ }
385
+ let result;
386
+ if (topology === undefined && (args.id !== undefined || args.title !== undefined)) {
387
+ // #28: forward `--blackboard`/`--topic` (plus the old build's
388
+ // objective/parent/phase reads) into the kernel input so a plain
389
+ // `multi-agent run <run> --id ma --blackboard bb --topic t` carries the
390
+ // blackboard linkage. Byte-exact to the old build's createMultiAgentRun
391
+ // option map (orchestrator/multi-agent-operations.ts).
392
+ result = mai.createMultiAgentRun(run, {
393
+ id: optionalString(args.id),
394
+ title: optionalString(args.title),
395
+ objective: optionalString(args.objective ?? args.reason),
396
+ parentMultiAgentRunId: optionalString(args.parent ?? args.parentMultiAgentRunId),
397
+ phase: optionalString(args.phase),
398
+ phaseId: optionalString(args.phaseId),
399
+ blackboardId: blackboardIdArg(args),
400
+ topicIds: topicIdsArg(args),
401
+ });
402
+ }
403
+ else {
404
+ result = host.hostRun(run, args);
405
+ }
360
406
  persist(run);
361
- return record;
362
- }
363
- if (!isHostRun && transitionId && !status && args.title === undefined && args.objective === undefined && args.id === undefined) {
364
- // `multi-agent run <run> <id>` (positional id, no status/create flags) —
365
- // SHOW the MultiAgentRun record (old handler's showMultiAgentRun arm).
366
- const record = (0, runtime_1.getMultiAgentRun)(run, transitionId);
367
- if (!record)
368
- throw new Error(`Unknown MultiAgentRun id: ${transitionId}`);
369
- return record;
370
- }
371
- let result;
372
- if (topology === undefined && (args.id !== undefined || args.title !== undefined)) {
373
- // #28: forward `--blackboard`/`--topic` (plus the old build's
374
- // objective/parent/phase reads) into the kernel input so a plain
375
- // `multi-agent run <run> --id ma --blackboard bb --topic t` carries the
376
- // blackboard linkage. Byte-exact to the old build's createMultiAgentRun
377
- // option map (orchestrator/multi-agent-operations.ts).
378
- result = mai.createMultiAgentRun(run, {
379
- id: optionalString(args.id),
380
- title: optionalString(args.title),
381
- objective: optionalString(args.objective ?? args.reason),
382
- parentMultiAgentRunId: optionalString(args.parent ?? args.parentMultiAgentRunId),
383
- phase: optionalString(args.phase),
384
- phaseId: optionalString(args.phaseId),
385
- blackboardId: blackboardIdArg(args),
386
- topicIds: topicIdsArg(args),
387
- });
388
- }
389
- else {
390
- result = host.hostRun(run, args);
391
- }
392
- persist(run);
393
- return result;
407
+ return result;
408
+ });
394
409
  }
395
410
  function multiAgentStatusCli(args) {
396
411
  const runId = requireArg(args.runId, "run id");
@@ -447,14 +462,15 @@ function multiAgentEvidenceCli(args) {
447
462
  * the old runner.multiAgentReasoning / multiAgentReasoningRefresh. */
448
463
  function multiAgentReasoningCli(args) {
449
464
  const runId = requireArg(args.runId, "run id");
450
- const run = loadRun(args, runId);
451
465
  const evidenceId = optionalString(args.evidence ?? args.evidenceId ?? args.id);
452
466
  if (args.refresh && !evidenceId) {
453
- const index = reasoning.refreshEvidenceReasoning(run);
454
- persist(run);
455
- return index;
467
+ return mutateRun(args, runId, (run) => {
468
+ const index = reasoning.refreshEvidenceReasoning(run);
469
+ persist(run);
470
+ return index;
471
+ });
456
472
  }
457
- return reasoning.showEvidenceReasoning(run, { evidenceId });
473
+ return reasoning.showEvidenceReasoning(loadRun(args, runId), { evidenceId });
458
474
  }
459
475
  function multiAgentReasoningText(args) {
460
476
  const runId = requireArg(args.runId, "run id");
@@ -464,10 +480,11 @@ function multiAgentReasoningText(args) {
464
480
  }
465
481
  function multiAgentReasoningRefreshCli(args) {
466
482
  const runId = requireArg(args.runId, "run id");
467
- const run = loadRun(args, runId);
468
- const index = reasoning.refreshEvidenceReasoning(run);
469
- persist(run);
470
- return index;
483
+ return mutateRun(args, runId, (run) => {
484
+ const index = reasoning.refreshEvidenceReasoning(run);
485
+ persist(run);
486
+ return index;
487
+ });
471
488
  }
472
489
  function multiAgentEvidenceText(args) {
473
490
  const runId = requireArg(args.runId, "run id");
@@ -481,182 +498,197 @@ function multiAgentEvidenceText(args) {
481
498
  }
482
499
  function multiAgentStepCli(args) {
483
500
  const runId = requireArg(args.runId, "run id");
484
- const run = loadRun(args, runId);
485
- const result = host.hostStep(run, args);
486
- persist(run);
487
- return result;
501
+ return mutateRun(args, runId, (run) => {
502
+ const result = host.hostStep(run, args);
503
+ persist(run);
504
+ return result;
505
+ });
488
506
  }
489
507
  function multiAgentBlackboardCli(args, action) {
490
508
  const runId = requireArg(args.runId, "run id");
491
- const run = loadRun(args, runId);
492
- const result = host.hostBlackboard(run, action, args);
493
- persist(run);
494
- return result;
509
+ return mutateRun(args, runId, (run) => {
510
+ const result = host.hostBlackboard(run, action, args);
511
+ persist(run);
512
+ return result;
513
+ });
495
514
  }
496
515
  function multiAgentScoreCli(args) {
497
516
  const runId = requireArg(args.runId, "run id");
498
- const run = loadRun(args, runId);
499
- const result = host.hostScore(run, args);
500
- persist(run);
501
- return result;
517
+ return mutateRun(args, runId, (run) => {
518
+ const result = host.hostScore(run, args);
519
+ persist(run);
520
+ return result;
521
+ });
502
522
  }
503
523
  function multiAgentSelectCli(args) {
504
524
  const runId = requireArg(args.runId, "run id");
505
- const run = loadRun(args, runId);
506
- const result = host.hostSelect(run, args);
507
- persist(run);
508
- return result;
525
+ return mutateRun(args, runId, (run) => {
526
+ const result = host.hostSelect(run, args);
527
+ persist(run);
528
+ return result;
529
+ });
509
530
  }
510
531
  function multiAgentRoleCli(args) {
511
532
  const runId = requireArg(args.runId, "run id");
512
- const run = loadRun(args, runId);
513
- const roleId = optionalString(args.roleId);
514
- const multiAgentRunId = multiAgentRunArg(args);
515
- if (roleId && !args.id && !multiAgentRunId) {
516
- const role = (0, runtime_1.getAgentRole)(run, roleId);
517
- if (!role)
518
- throw new Error(`Unknown AgentRole id: ${roleId}`);
533
+ return mutateRun(args, runId, (run) => {
534
+ const roleId = optionalString(args.roleId);
535
+ const multiAgentRunId = multiAgentRunArg(args);
536
+ if (roleId && !args.id && !multiAgentRunId) {
537
+ const role = (0, runtime_1.getAgentRole)(run, roleId);
538
+ if (!role)
539
+ throw new Error(`Unknown AgentRole id: ${roleId}`);
540
+ return role;
541
+ }
542
+ // #29: parseArgv keeps `--required-evidence` etc. as their literal kebab
543
+ // keys, so the old camelCase-only reads folded to []. Read BOTH the kebab
544
+ // CLI key and the camelCase MCP alias, byte-exact to the old build's
545
+ // createAgentRole option map (orchestrator/multi-agent-operations.ts).
546
+ const role = mai.createAgentRole(run, {
547
+ id: optionalString(args.id) ?? roleId,
548
+ multiAgentRunId: requireArg(multiAgentRunId, "multi-agent run id"),
549
+ title: optionalString(args.title),
550
+ responsibilities: arrayArg(args.responsibility ?? args.responsibilities),
551
+ requiredEvidence: arrayArg(args.requiredEvidence ?? args["required-evidence"]),
552
+ sandboxProfileHints: arrayArg(args.sandbox ?? args.sandboxProfile ?? args.sandboxProfileHint ?? args["sandbox-profile"]),
553
+ expectedArtifacts: arrayArg(args.expectedArtifact ?? args.expectedArtifacts ?? args["expected-artifact"]),
554
+ faninObligations: arrayArg(args.faninObligation ?? args.faninObligations ?? args["fanin-obligation"]),
555
+ parentRoleId: optionalString(args.parent ?? args.parentRoleId),
556
+ blackboardId: blackboardIdArg(args),
557
+ topicIds: topicIdsArg(args),
558
+ });
559
+ persist(run);
519
560
  return role;
520
- }
521
- // #29: parseArgv keeps `--required-evidence` etc. as their literal kebab
522
- // keys, so the old camelCase-only reads folded to []. Read BOTH the kebab
523
- // CLI key and the camelCase MCP alias, byte-exact to the old build's
524
- // createAgentRole option map (orchestrator/multi-agent-operations.ts).
525
- const role = mai.createAgentRole(run, {
526
- id: optionalString(args.id) ?? roleId,
527
- multiAgentRunId: requireArg(multiAgentRunId, "multi-agent run id"),
528
- title: optionalString(args.title),
529
- responsibilities: arrayArg(args.responsibility ?? args.responsibilities),
530
- requiredEvidence: arrayArg(args.requiredEvidence ?? args["required-evidence"]),
531
- sandboxProfileHints: arrayArg(args.sandbox ?? args.sandboxProfile ?? args.sandboxProfileHint ?? args["sandbox-profile"]),
532
- expectedArtifacts: arrayArg(args.expectedArtifact ?? args.expectedArtifacts ?? args["expected-artifact"]),
533
- faninObligations: arrayArg(args.faninObligation ?? args.faninObligations ?? args["fanin-obligation"]),
534
- parentRoleId: optionalString(args.parent ?? args.parentRoleId),
535
- blackboardId: blackboardIdArg(args),
536
- topicIds: topicIdsArg(args),
537
561
  });
538
- persist(run);
539
- return role;
540
562
  }
541
563
  function multiAgentGroupCli(args) {
542
564
  const runId = requireArg(args.runId, "run id");
543
- const run = loadRun(args, runId);
544
- const groupId = optionalString(args.groupId);
545
- const multiAgentRunId = multiAgentRunArg(args);
546
- if (groupId && !args.id && !multiAgentRunId) {
547
- const group = (0, runtime_1.getAgentGroup)(run, groupId);
548
- if (!group)
549
- throw new Error(`Unknown AgentGroup id: ${groupId}`);
565
+ return mutateRun(args, runId, (run) => {
566
+ const groupId = optionalString(args.groupId);
567
+ const multiAgentRunId = multiAgentRunArg(args);
568
+ if (groupId && !args.id && !multiAgentRunId) {
569
+ const group = (0, runtime_1.getAgentGroup)(run, groupId);
570
+ if (!group)
571
+ throw new Error(`Unknown AgentGroup id: ${groupId}`);
572
+ return group;
573
+ }
574
+ // #28: forward `--blackboard`/`--topic` (plus phaseId/parent) into the
575
+ // kernel input. Byte-exact to the old createAgentGroup option map.
576
+ const group = mai.createAgentGroup(run, {
577
+ id: optionalString(args.id) ?? groupId,
578
+ multiAgentRunId: requireArg(multiAgentRunId, "multi-agent run id"),
579
+ title: optionalString(args.title),
580
+ phase: optionalString(args.phase),
581
+ phaseId: optionalString(args.phaseId),
582
+ taskIds: arrayArg(args.task ?? args.taskId ?? args.tasks),
583
+ parentGroupId: optionalString(args.parent ?? args.parentGroupId),
584
+ blackboardId: blackboardIdArg(args),
585
+ topicIds: topicIdsArg(args),
586
+ });
587
+ persist(run);
550
588
  return group;
551
- }
552
- // #28: forward `--blackboard`/`--topic` (plus phaseId/parent) into the
553
- // kernel input. Byte-exact to the old createAgentGroup option map.
554
- const group = mai.createAgentGroup(run, {
555
- id: optionalString(args.id) ?? groupId,
556
- multiAgentRunId: requireArg(multiAgentRunId, "multi-agent run id"),
557
- title: optionalString(args.title),
558
- phase: optionalString(args.phase),
559
- phaseId: optionalString(args.phaseId),
560
- taskIds: arrayArg(args.task ?? args.taskId ?? args.tasks),
561
- parentGroupId: optionalString(args.parent ?? args.parentGroupId),
562
- blackboardId: blackboardIdArg(args),
563
- topicIds: topicIdsArg(args),
564
589
  });
565
- persist(run);
566
- return group;
567
590
  }
568
591
  function multiAgentMembershipCli(args) {
569
592
  const runId = requireArg(args.runId, "run id");
570
- const run = loadRun(args, runId);
571
- const membershipId = optionalString(args.membershipId);
572
- const groupId = groupArg(args);
573
- const roleId = optionalString(args.roleId ?? args.role);
574
- if (membershipId && !args.id && !groupId && !roleId) {
575
- const membership = (0, runtime_1.getAgentMembership)(run, membershipId);
576
- if (!membership)
577
- throw new Error(`Unknown AgentMembership id: ${membershipId}`);
593
+ return mutateRun(args, runId, (run) => {
594
+ const membershipId = optionalString(args.membershipId);
595
+ const groupId = groupArg(args);
596
+ const roleId = optionalString(args.roleId ?? args.role);
597
+ if (membershipId && !args.id && !groupId && !roleId) {
598
+ const membership = (0, runtime_1.getAgentMembership)(run, membershipId);
599
+ if (!membership)
600
+ throw new Error(`Unknown AgentMembership id: ${membershipId}`);
601
+ return membership;
602
+ }
603
+ // #28: forward `--blackboard`/`--topic` (plus multiAgentRunId/status) into
604
+ // the kernel input. Byte-exact to the old assignAgentMembership option map.
605
+ const membership = mai.assignAgentMembership(run, {
606
+ id: optionalString(args.id) ?? membershipId,
607
+ multiAgentRunId: multiAgentRunArg(args),
608
+ groupId: requireArg(groupId, "group id"),
609
+ roleId: requireArg(roleId, "role id"),
610
+ taskId: requireArg(args.taskId ?? args.task, "task id"),
611
+ workerId: optionalString(args.workerId ?? args.worker),
612
+ dispatchId: optionalString(args.dispatchId ?? args.dispatch),
613
+ fanoutId: optionalString(args.fanoutId ?? args.fanout ?? args["multi-agent-fanout"]),
614
+ status: optionalString(args.status),
615
+ blackboardId: blackboardIdArg(args),
616
+ topicIds: topicIdsArg(args),
617
+ });
618
+ persist(run);
578
619
  return membership;
579
- }
580
- // #28: forward `--blackboard`/`--topic` (plus multiAgentRunId/status) into
581
- // the kernel input. Byte-exact to the old assignAgentMembership option map.
582
- const membership = mai.assignAgentMembership(run, {
583
- id: optionalString(args.id) ?? membershipId,
584
- multiAgentRunId: multiAgentRunArg(args),
585
- groupId: requireArg(groupId, "group id"),
586
- roleId: requireArg(roleId, "role id"),
587
- taskId: requireArg(args.taskId ?? args.task, "task id"),
588
- workerId: optionalString(args.workerId ?? args.worker),
589
- dispatchId: optionalString(args.dispatchId ?? args.dispatch),
590
- fanoutId: optionalString(args.fanoutId ?? args.fanout ?? args["multi-agent-fanout"]),
591
- status: optionalString(args.status),
592
- blackboardId: blackboardIdArg(args),
593
- topicIds: topicIdsArg(args),
594
620
  });
595
- persist(run);
596
- return membership;
597
621
  }
598
622
  function multiAgentFanoutCli(args) {
599
623
  const runId = requireArg(args.runId, "run id");
600
- const run = loadRun(args, runId);
601
- const fanoutId = optionalString(args.fanoutId);
602
- const groupId = groupArg(args);
603
- if (fanoutId && !args.id && !groupId) {
604
- const fanout = (0, runtime_1.getAgentFanout)(run, fanoutId);
605
- if (!fanout)
606
- throw new Error(`Unknown AgentFanout id: ${fanoutId}`);
624
+ return mutateRun(args, runId, (run) => {
625
+ const fanoutId = optionalString(args.fanoutId);
626
+ const groupId = groupArg(args);
627
+ if (fanoutId && !args.id && !groupId) {
628
+ const fanout = (0, runtime_1.getAgentFanout)(run, fanoutId);
629
+ if (!fanout)
630
+ throw new Error(`Unknown AgentFanout id: ${fanoutId}`);
631
+ return fanout;
632
+ }
633
+ // #28: forward `--blackboard`/`--topic` so `fanout.blackboardId` inherits
634
+ // the board (kernel: input.blackboardId || group.blackboardId ||
635
+ // multiAgentRun.blackboardId). Also port the old build's fuller fanout
636
+ // reads (multiAgentRunId, workerIds/membershipIds/dispatchIds,
637
+ // sandboxProfileChoices, expectedReturnShape).
638
+ const fanout = mai.createAgentFanout(run, {
639
+ id: optionalString(args.id) ?? fanoutId,
640
+ multiAgentRunId: multiAgentRunArg(args),
641
+ groupId: requireArg(groupId, "group id"),
642
+ reason: requireArg(args.reason, "reason"),
643
+ roleIds: arrayArg(args.role ?? args.roleId ?? args.roles),
644
+ taskIds: arrayArg(args.task ?? args.taskId ?? args.tasks),
645
+ workerIds: arrayArg(args.worker ?? args.workerId ?? args.workers),
646
+ membershipIds: arrayArg(args.membership ?? args.membershipId ?? args.memberships),
647
+ dispatchIds: arrayArg(args.dispatch ?? args.dispatchId ?? args.dispatches),
648
+ // This one flag (aliased --limit/--concurrency/--concurrencyLimit) goes
649
+ // through requiredNumberFlag, not the file's own lenient numberArg — a
650
+ // bare flag here used to silently mean "no limit" (fan out unbounded),
651
+ // the opposite of the "silently means 1" bug the same audit found in
652
+ // metrics-cli.ts/registry-cli.ts's own --limit handling; both now fail
653
+ // loud instead of guessing in either direction.
654
+ concurrencyLimit: (0, numeric_flag_1.requiredNumberFlag)(args.limit ?? args.concurrency ?? args.concurrencyLimit, "--limit"),
655
+ sandboxProfileChoices: parseSandboxChoicesCli(args),
656
+ expectedReturnShape: optionalString(args.expectedReturnShape ?? args["expected-return-shape"]),
657
+ blackboardId: blackboardIdArg(args),
658
+ topicIds: topicIdsArg(args),
659
+ });
660
+ persist(run);
607
661
  return fanout;
608
- }
609
- // #28: forward `--blackboard`/`--topic` so `fanout.blackboardId` inherits
610
- // the board (kernel: input.blackboardId || group.blackboardId ||
611
- // multiAgentRun.blackboardId). Also port the old build's fuller fanout
612
- // reads (multiAgentRunId, workerIds/membershipIds/dispatchIds,
613
- // sandboxProfileChoices, expectedReturnShape).
614
- const fanout = mai.createAgentFanout(run, {
615
- id: optionalString(args.id) ?? fanoutId,
616
- multiAgentRunId: multiAgentRunArg(args),
617
- groupId: requireArg(groupId, "group id"),
618
- reason: requireArg(args.reason, "reason"),
619
- roleIds: arrayArg(args.role ?? args.roleId ?? args.roles),
620
- taskIds: arrayArg(args.task ?? args.taskId ?? args.tasks),
621
- workerIds: arrayArg(args.worker ?? args.workerId ?? args.workers),
622
- membershipIds: arrayArg(args.membership ?? args.membershipId ?? args.memberships),
623
- dispatchIds: arrayArg(args.dispatch ?? args.dispatchId ?? args.dispatches),
624
- concurrencyLimit: numberArg(args.limit ?? args.concurrency ?? args.concurrencyLimit),
625
- sandboxProfileChoices: parseSandboxChoicesCli(args),
626
- expectedReturnShape: optionalString(args.expectedReturnShape ?? args["expected-return-shape"]),
627
- blackboardId: blackboardIdArg(args),
628
- topicIds: topicIdsArg(args),
629
662
  });
630
- persist(run);
631
- return fanout;
632
663
  }
633
664
  function multiAgentFaninCli(args) {
634
665
  const runId = requireArg(args.runId, "run id");
635
- const run = loadRun(args, runId);
636
- const faninId = optionalString(args.faninId);
637
- const groupId = groupArg(args);
638
- const fanoutId = optionalString(args.fanoutId ?? args.fanout);
639
- if (faninId && !args.id && !groupId && !fanoutId) {
640
- const fanin = (0, runtime_1.getAgentFanin)(run, faninId);
641
- if (!fanin)
642
- throw new Error(`Unknown AgentFanin id: ${faninId}`);
666
+ return mutateRun(args, runId, (run) => {
667
+ const faninId = optionalString(args.faninId);
668
+ const groupId = groupArg(args);
669
+ const fanoutId = optionalString(args.fanoutId ?? args.fanout);
670
+ if (faninId && !args.id && !groupId && !fanoutId) {
671
+ const fanin = (0, runtime_1.getAgentFanin)(run, faninId);
672
+ if (!fanin)
673
+ throw new Error(`Unknown AgentFanin id: ${faninId}`);
674
+ return fanin;
675
+ }
676
+ // #29: `--required-role` folds to the kebab key `required-role`; read the
677
+ // kebab + camelCase aliases. #28: forward `--blackboard`/`--topic` +
678
+ // multiAgentRunId. Byte-exact to the old collectAgentFanin option map.
679
+ const fanin = mai.collectAgentFanin(run, {
680
+ id: optionalString(args.id) ?? faninId,
681
+ multiAgentRunId: multiAgentRunArg(args),
682
+ groupId,
683
+ fanoutId,
684
+ requiredRoleIds: arrayArg(args.requiredRole ?? args.requiredRoleId ?? args["required-role"]),
685
+ strategy: optionalString(args.strategy),
686
+ blackboardId: blackboardIdArg(args),
687
+ topicIds: topicIdsArg(args),
688
+ });
689
+ persist(run);
643
690
  return fanin;
644
- }
645
- // #29: `--required-role` folds to the kebab key `required-role`; read the
646
- // kebab + camelCase aliases. #28: forward `--blackboard`/`--topic` +
647
- // multiAgentRunId. Byte-exact to the old collectAgentFanin option map.
648
- const fanin = mai.collectAgentFanin(run, {
649
- id: optionalString(args.id) ?? faninId,
650
- multiAgentRunId: multiAgentRunArg(args),
651
- groupId,
652
- fanoutId,
653
- requiredRoleIds: arrayArg(args.requiredRole ?? args.requiredRoleId ?? args["required-role"]),
654
- strategy: optionalString(args.strategy),
655
- blackboardId: blackboardIdArg(args),
656
- topicIds: topicIdsArg(args),
657
691
  });
658
- persist(run);
659
- return fanin;
660
692
  }
661
693
  function multiAgentShowCli(args, id) {
662
694
  const runId = requireArg(args.runId, "run id");
@@ -736,24 +768,27 @@ function blackboardGraphCli(args) {
736
768
  }
737
769
  function blackboardResolveCli(args) {
738
770
  const runId = requireArg(args.runId, "run id");
739
- const run = loadRun(args, runId);
740
- const board = coord.resolveBlackboard(run, { id: optionalString(args.id), title: optionalString(args.title), multiAgentRunId: optionalString(args.multiAgentRunId), groupId: optionalString(args.groupId), roleId: optionalString(args.roleId), membershipId: optionalString(args.membershipId) });
741
- persist(run);
742
- return board;
771
+ return mutateRun(args, runId, (run) => {
772
+ const board = coord.resolveBlackboard(run, { id: optionalString(args.id), title: optionalString(args.title), multiAgentRunId: optionalString(args.multiAgentRunId), groupId: optionalString(args.groupId), roleId: optionalString(args.roleId), membershipId: optionalString(args.membershipId) });
773
+ persist(run);
774
+ return board;
775
+ });
743
776
  }
744
777
  function blackboardTopicCreateCli(args) {
745
778
  const runId = requireArg(args.runId, "run id");
746
- const run = loadRun(args, runId);
747
- const topic = coord.createBlackboardTopic(run, { id: optionalString(args.id), title: requireArg(args.title, "topic title"), description: optionalString(args.description), blackboardId: optionalString(args.blackboardId), author: parseBlackboardAuthorCli(args), scope: parseBlackboardScopeCli(args), tags: arrayArg(args.tag) });
748
- persist(run);
749
- return topic;
779
+ return mutateRun(args, runId, (run) => {
780
+ const topic = coord.createBlackboardTopic(run, { id: optionalString(args.id), title: requireArg(args.title, "topic title"), description: optionalString(args.description), blackboardId: optionalString(args.blackboardId), author: parseBlackboardAuthorCli(args), scope: parseBlackboardScopeCli(args), tags: arrayArg(args.tag) });
781
+ persist(run);
782
+ return topic;
783
+ });
750
784
  }
751
785
  function blackboardMessagePostCli(args) {
752
786
  const runId = requireArg(args.runId, "run id");
753
- const run = loadRun(args, runId);
754
- const message = coord.postBlackboardMessage(run, { id: optionalString(args.id), topicId: requireArg(args.topic ?? args.topicId, "topic id"), blackboardId: optionalString(args.blackboardId), body: requireArg(args.body, "message body"), replyToId: optionalString(args.replyTo), visibility: optionalString(args.visibility), author: parseBlackboardAuthorCli(args), scope: parseBlackboardScopeCli(args), links: parseBlackboardLinksCli(runId, args), tags: arrayArg(args.tag), evidenceRefs: arrayArg(args.evidence), artifactRefIds: arrayArg(args.artifact) });
755
- persist(run);
756
- return message;
787
+ return mutateRun(args, runId, (run) => {
788
+ const message = coord.postBlackboardMessage(run, { id: optionalString(args.id), topicId: requireArg(args.topic ?? args.topicId, "topic id"), blackboardId: optionalString(args.blackboardId), body: requireArg(args.body, "message body"), replyToId: optionalString(args.replyTo), visibility: optionalString(args.visibility), author: parseBlackboardAuthorCli(args), scope: parseBlackboardScopeCli(args), links: parseBlackboardLinksCli(runId, args), tags: arrayArg(args.tag), evidenceRefs: arrayArg(args.evidence), artifactRefIds: arrayArg(args.artifact) });
789
+ persist(run);
790
+ return message;
791
+ });
757
792
  }
758
793
  function blackboardMessageListCli(args) {
759
794
  const runId = requireArg(args.runId, "run id");
@@ -762,17 +797,19 @@ function blackboardMessageListCli(args) {
762
797
  }
763
798
  function blackboardContextPutCli(args) {
764
799
  const runId = requireArg(args.runId, "run id");
765
- const run = loadRun(args, runId);
766
- const context = coord.putBlackboardContext(run, { id: optionalString(args.id), topicId: requireArg(args.topic ?? args.topicId, "topic id"), kind: requireArg(args.kind, "context kind"), key: optionalString(args.key), value: requireArg(args.value ?? args.body, "context value"), blackboardId: optionalString(args.blackboardId), supersedesContextIds: arrayArg(args.supersedes), author: parseBlackboardAuthorCli(args), scope: parseBlackboardScopeCli(args), links: parseBlackboardLinksCli(runId, args), tags: arrayArg(args.tag), evidenceRefs: arrayArg(args.evidence), artifactRefIds: arrayArg(args.artifact) });
767
- persist(run);
768
- return context;
800
+ return mutateRun(args, runId, (run) => {
801
+ const context = coord.putBlackboardContext(run, { id: optionalString(args.id), topicId: requireArg(args.topic ?? args.topicId, "topic id"), kind: requireArg(args.kind, "context kind"), key: optionalString(args.key), value: requireArg(args.value ?? args.body, "context value"), blackboardId: optionalString(args.blackboardId), supersedesContextIds: arrayArg(args.supersedes), author: parseBlackboardAuthorCli(args), scope: parseBlackboardScopeCli(args), links: parseBlackboardLinksCli(runId, args), tags: arrayArg(args.tag), evidenceRefs: arrayArg(args.evidence), artifactRefIds: arrayArg(args.artifact) });
802
+ persist(run);
803
+ return context;
804
+ });
769
805
  }
770
806
  function blackboardArtifactAddCli(args) {
771
807
  const runId = requireArg(args.runId, "run id");
772
- const run = loadRun(args, runId);
773
- const artifact = coord.addBlackboardArtifact(run, { id: optionalString(args.id), topicId: optionalString(args.topic ?? args.topicId), kind: requireArg(args.kind, "artifact kind"), path: optionalString(args.path), locator: optionalString(args.locator), blackboardId: optionalString(args.blackboardId), source: optionalString(args.source), author: parseBlackboardAuthorCli(args), scope: parseBlackboardScopeCli(args), links: parseBlackboardLinksCli(runId, args), tags: arrayArg(args.tag), evidenceRefs: arrayArg(args.evidence) });
774
- persist(run);
775
- return artifact;
808
+ return mutateRun(args, runId, (run) => {
809
+ const artifact = coord.addBlackboardArtifact(run, { id: optionalString(args.id), topicId: optionalString(args.topic ?? args.topicId), kind: requireArg(args.kind, "artifact kind"), path: optionalString(args.path), locator: optionalString(args.locator), blackboardId: optionalString(args.blackboardId), source: optionalString(args.source), author: parseBlackboardAuthorCli(args), scope: parseBlackboardScopeCli(args), links: parseBlackboardLinksCli(runId, args), tags: arrayArg(args.tag), evidenceRefs: arrayArg(args.evidence) });
810
+ persist(run);
811
+ return artifact;
812
+ });
776
813
  }
777
814
  function blackboardArtifactListCli(args) {
778
815
  const runId = requireArg(args.runId, "run id");
@@ -781,10 +818,11 @@ function blackboardArtifactListCli(args) {
781
818
  }
782
819
  function blackboardSnapshotCli(args) {
783
820
  const runId = requireArg(args.runId, "run id");
784
- const run = loadRun(args, runId);
785
- const snapshot = coord.createBlackboardSnapshot(run, optionalString(args.blackboardId));
786
- persist(run);
787
- return snapshot;
821
+ return mutateRun(args, runId, (run) => {
822
+ const snapshot = coord.createBlackboardSnapshot(run, optionalString(args.blackboardId));
823
+ persist(run);
824
+ return snapshot;
825
+ });
788
826
  }
789
827
  function coordinatorSummaryCli(args) {
790
828
  const runId = requireArg(args.runId, "run id");
@@ -793,10 +831,11 @@ function coordinatorSummaryCli(args) {
793
831
  }
794
832
  function coordinatorDecisionCli(args) {
795
833
  const runId = requireArg(args.runId, "run id");
796
- const run = loadRun(args, runId);
797
- const decision = coord.recordCoordinatorDecision(run, { id: optionalString(args.id), blackboardId: optionalString(args.blackboardId), kind: requireArg(args.kind, "decision kind"), outcome: requireArg(args.outcome, "decision outcome"), reason: requireArg(args.reason, "decision reason"), subjectIds: arrayArg(args.subject), evidenceRefs: arrayArg(args.evidence), artifactRefIds: arrayArg(args.artifact), messageIds: arrayArg(args.message) });
798
- persist(run);
799
- return decision;
834
+ return mutateRun(args, runId, (run) => {
835
+ const decision = coord.recordCoordinatorDecision(run, { id: optionalString(args.id), blackboardId: optionalString(args.blackboardId), kind: requireArg(args.kind, "decision kind"), outcome: requireArg(args.outcome, "decision outcome"), reason: requireArg(args.reason, "decision reason"), subjectIds: arrayArg(args.subject), evidenceRefs: arrayArg(args.evidence), artifactRefIds: arrayArg(args.artifact), messageIds: arrayArg(args.message) });
836
+ persist(run);
837
+ return decision;
838
+ });
800
839
  }
801
840
  // ---------------------------------------------------------------------------
802
841
  // Candidate scoring
@@ -816,33 +855,34 @@ function candidateShowCli(args, candidateId) {
816
855
  }
817
856
  function candidateRegisterCli(args) {
818
857
  const runId = requireArg(args.runId, "run id");
819
- const run = loadRun(args, runId);
820
- // `candidate register --worker <id>` — derive the worker's accepted
821
- // result/verifier state nodes (and result path) from the worker scope +
822
- // its backing task, so a candidate registered from a verified worker
823
- // carries the verifier gate the selection gate requires. Port of the old
824
- // orchestrator/candidate-operations.ts registerCandidate worker read
825
- // (v2's candidateRegisterCli only forwarded --result-node/--verifier-node).
826
- const workerId = optionalString(args.worker ?? args.workerId);
827
- const worker = workerId ? (0, worker_isolation_1.getWorkerScope)(run, workerId) : undefined;
828
- if (workerId && !worker)
829
- throw new Error(`Unknown worker id for run ${run.id}: ${workerId}`);
830
- const workerOutput = worker?.output;
831
- const task = worker ? run.tasks.find((entry) => entry.id === worker.taskId) : undefined;
832
- const resultNodeId = optionalString(args.resultNode) || worker?.resultNodeId || task?.resultNodeId;
833
- const verifierNodeId = optionalString(args.verifierNode) || workerOutput?.verifierNodeId || task?.verifierNodeId;
834
- const resultPath = optionalString(args.resultPath) || workerOutput?.resultPath || task?.resultPath;
835
- const candidate = cs.registerCandidate(run, {
836
- id: optionalString(args.id),
837
- kind: optionalString(args.kind),
838
- workerId,
839
- taskId: optionalString(args.task ?? args.taskId) || worker?.taskId,
840
- resultNodeId,
841
- verifierNodeId,
842
- resultPath,
858
+ return mutateRun(args, runId, (run) => {
859
+ // `candidate register --worker <id>` — derive the worker's accepted
860
+ // result/verifier state nodes (and result path) from the worker scope +
861
+ // its backing task, so a candidate registered from a verified worker
862
+ // carries the verifier gate the selection gate requires. Port of the old
863
+ // orchestrator/candidate-operations.ts registerCandidate worker read
864
+ // (v2's candidateRegisterCli only forwarded --result-node/--verifier-node).
865
+ const workerId = optionalString(args.worker ?? args.workerId);
866
+ const worker = workerId ? (0, worker_isolation_1.getWorkerScope)(run, workerId) : undefined;
867
+ if (workerId && !worker)
868
+ throw new Error(`Unknown worker id for run ${run.id}: ${workerId}`);
869
+ const workerOutput = worker?.output;
870
+ const task = worker ? run.tasks.find((entry) => entry.id === worker.taskId) : undefined;
871
+ const resultNodeId = optionalString(args.resultNode) || worker?.resultNodeId || task?.resultNodeId;
872
+ const verifierNodeId = optionalString(args.verifierNode) || workerOutput?.verifierNodeId || task?.verifierNodeId;
873
+ const resultPath = optionalString(args.resultPath) || workerOutput?.resultPath || task?.resultPath;
874
+ const candidate = cs.registerCandidate(run, {
875
+ id: optionalString(args.id),
876
+ kind: optionalString(args.kind),
877
+ workerId,
878
+ taskId: optionalString(args.task ?? args.taskId) || worker?.taskId,
879
+ resultNodeId,
880
+ verifierNodeId,
881
+ resultPath,
882
+ });
883
+ persist(run);
884
+ return candidate;
843
885
  });
844
- persist(run);
845
- return candidate;
846
886
  }
847
887
  function parseCriteriaCli(args) {
848
888
  const criteria = {};
@@ -868,32 +908,36 @@ function parseCriteriaCli(args) {
868
908
  }
869
909
  function candidateScoreCli(args, candidateId) {
870
910
  const runId = requireArg(args.runId, "run id");
871
- const run = loadRun(args, runId);
872
- const evidence = arrayArg(args.evidence).map((entry, index) => ({ id: `score:${index + 1}`, source: "cli", locator: entry, summary: entry }));
873
- const score = cs.scoreCandidate(run, candidateId, { id: optionalString(args.id), scorer: optionalString(args.scorer), criteria: parseCriteriaCli(args), maxTotal: numberArg(args.maxTotal ?? args.max), verdict: optionalString(args.verdict), evidence, notes: optionalString(args.notes) });
874
- persist(run);
875
- return score;
911
+ return mutateRun(args, runId, (run) => {
912
+ const evidence = arrayArg(args.evidence).map((entry, index) => ({ id: `score:${index + 1}`, source: "cli", locator: entry, summary: entry }));
913
+ const score = cs.scoreCandidate(run, candidateId, { id: optionalString(args.id), scorer: optionalString(args.scorer), criteria: parseCriteriaCli(args), maxTotal: numberArg(args.maxTotal ?? args.max), verdict: optionalString(args.verdict), evidence, notes: optionalString(args.notes) });
914
+ persist(run);
915
+ return score;
916
+ });
876
917
  }
877
918
  function candidateRankCli(args) {
878
919
  const runId = requireArg(args.runId, "run id");
879
- const run = loadRun(args, runId);
880
- const ranking = cs.rankCandidates(run, { includeRejected: boolArg(args.includeRejected), policy: { minNormalized: numberArg(args.minNormalized), requireEvidence: args.requireEvidence === undefined ? undefined : boolArg(args.requireEvidence), requireVerifierGate: args.requireVerifierGate === undefined ? undefined : boolArg(args.requireVerifierGate), tieBreaker: optionalString(args.tieBreaker) } });
881
- persist(run);
882
- return ranking;
920
+ return mutateRun(args, runId, (run) => {
921
+ const ranking = cs.rankCandidates(run, { includeRejected: boolArg(args.includeRejected), policy: { minNormalized: numberArg(args.minNormalized), requireEvidence: args.requireEvidence === undefined ? undefined : boolArg(args.requireEvidence), requireVerifierGate: args.requireVerifierGate === undefined ? undefined : boolArg(args.requireVerifierGate), tieBreaker: optionalString(args.tieBreaker) } });
922
+ persist(run);
923
+ return ranking;
924
+ });
883
925
  }
884
926
  function candidateSelectCli(args, candidateId) {
885
927
  const runId = requireArg(args.runId, "run id");
886
- const run = loadRun(args, runId);
887
- const selection = cs.selectCandidate(run, candidateId, { selectedBy: optionalString(args.selectedBy ?? args.by), reason: optionalString(args.reason), scoreId: optionalString(args.score), allowUnverified: boolArg(args.allowUnverified) }, { policy: { minNormalized: numberArg(args.minNormalized), requireVerifierGate: args.requireVerifierGate === undefined ? undefined : boolArg(args.requireVerifierGate) } });
888
- persist(run);
889
- return selection;
928
+ return mutateRun(args, runId, (run) => {
929
+ const selection = cs.selectCandidate(run, candidateId, { selectedBy: optionalString(args.selectedBy ?? args.by), reason: optionalString(args.reason), scoreId: optionalString(args.score), allowUnverified: boolArg(args.allowUnverified) }, { policy: { minNormalized: numberArg(args.minNormalized), requireVerifierGate: args.requireVerifierGate === undefined ? undefined : boolArg(args.requireVerifierGate) } });
930
+ persist(run);
931
+ return selection;
932
+ });
890
933
  }
891
934
  function candidateRejectCli(args, candidateId) {
892
935
  const runId = requireArg(args.runId, "run id");
893
- const run = loadRun(args, runId);
894
- const candidate = cs.rejectCandidate(run, candidateId, requireArg(args.reason, "reject reason"));
895
- persist(run);
896
- return candidate;
936
+ return mutateRun(args, runId, (run) => {
937
+ const candidate = cs.rejectCandidate(run, candidateId, requireArg(args.reason, "reject reason"));
938
+ persist(run);
939
+ return candidate;
940
+ });
897
941
  }
898
942
  function candidateSummaryCli(args) {
899
943
  const runId = requireArg(args.runId, "run id");
@@ -915,24 +959,24 @@ function targetFromArgs(args, positionalKind, positionalId) {
915
959
  }
916
960
  function approveCli(args, positionalKind, positionalId) {
917
961
  const runId = requireArg(args.runId, "run id");
918
- const run = loadRun(args, runId);
919
- const target = targetFromArgs(args, positionalKind, positionalId);
920
- const record = collab.recordApproval(run, { target: target, decision: "approve", actor: optionalString(args.actor), actorKind: optionalString(args.actorKind), role: optionalString(args.role), displayName: optionalString(args.displayName), attested: boolArg(args.attested), attestation: optionalString(args.attestation), rationale: optionalString(args.rationale), supersedes: optionalString(args.supersedes) });
921
- return record;
962
+ return mutateRun(args, runId, (run) => {
963
+ const target = targetFromArgs(args, positionalKind, positionalId);
964
+ return collab.recordApproval(run, { target: target, decision: "approve", actor: optionalString(args.actor), actorKind: optionalString(args.actorKind), role: optionalString(args.role), displayName: optionalString(args.displayName), attested: boolArg(args.attested), attestation: optionalString(args.attestation), rationale: optionalString(args.rationale), supersedes: optionalString(args.supersedes) });
965
+ });
922
966
  }
923
967
  function rejectCollabCli(args, positionalKind, positionalId) {
924
968
  const runId = requireArg(args.runId, "run id");
925
- const run = loadRun(args, runId);
926
- const target = targetFromArgs(args, positionalKind, positionalId);
927
- const record = collab.recordApproval(run, { target: target, decision: "reject", actor: optionalString(args.actor), actorKind: optionalString(args.actorKind), role: optionalString(args.role), displayName: optionalString(args.displayName), attested: boolArg(args.attested), attestation: optionalString(args.attestation), rationale: optionalString(args.rationale) });
928
- return record;
969
+ return mutateRun(args, runId, (run) => {
970
+ const target = targetFromArgs(args, positionalKind, positionalId);
971
+ return collab.recordApproval(run, { target: target, decision: "reject", actor: optionalString(args.actor), actorKind: optionalString(args.actorKind), role: optionalString(args.role), displayName: optionalString(args.displayName), attested: boolArg(args.attested), attestation: optionalString(args.attestation), rationale: optionalString(args.rationale) });
972
+ });
929
973
  }
930
974
  function commentAddCli(args, positionalKind, positionalId) {
931
975
  const runId = requireArg(args.runId, "run id");
932
- const run = loadRun(args, runId);
933
- const target = targetFromArgs(args, positionalKind, positionalId);
934
- const record = collab.recordComment(run, { target: target, body: requireArg(args.body ?? args.message ?? args.text, "comment body"), actor: optionalString(args.actor), actorKind: optionalString(args.actorKind), role: optionalString(args.role), threadId: optionalString(args.thread), parentId: optionalString(args.parent) });
935
- return record;
976
+ return mutateRun(args, runId, (run) => {
977
+ const target = targetFromArgs(args, positionalKind, positionalId);
978
+ return collab.recordComment(run, { target: target, body: requireArg(args.body ?? args.message ?? args.text, "comment body"), actor: optionalString(args.actor), actorKind: optionalString(args.actorKind), role: optionalString(args.role), threadId: optionalString(args.thread), parentId: optionalString(args.parent) });
979
+ });
936
980
  }
937
981
  function commentListCli(args) {
938
982
  const runId = requireArg(args.runId, "run id");
@@ -943,10 +987,10 @@ function commentListCli(args) {
943
987
  }
944
988
  function handoffCli(args, positionalKind, positionalId) {
945
989
  const runId = requireArg(args.runId, "run id");
946
- const run = loadRun(args, runId);
947
- const target = targetFromArgs(args, positionalKind, positionalId);
948
- const record = collab.recordHandoff(run, { target: target, toActor: optionalString(args.to ?? args.toActor), toRole: optionalString(args.toRole), fromActor: optionalString(args.from), reason: requireArg(args.reason, "handoff reason"), actor: optionalString(args.actor), actorKind: optionalString(args.actorKind), role: optionalString(args.role) });
949
- return record;
990
+ return mutateRun(args, runId, (run) => {
991
+ const target = targetFromArgs(args, positionalKind, positionalId);
992
+ return collab.recordHandoff(run, { target: target, toActor: optionalString(args.to ?? args.toActor), toRole: optionalString(args.toRole), fromActor: optionalString(args.from), reason: requireArg(args.reason, "handoff reason"), actor: optionalString(args.actor), actorKind: optionalString(args.actorKind), role: optionalString(args.role) });
993
+ });
950
994
  }
951
995
  function reviewStatusCli(args) {
952
996
  const runId = requireArg(args.runId, "run id");
@@ -957,19 +1001,19 @@ function reviewStatusCli(args) {
957
1001
  }
958
1002
  function reviewPolicyCli(args) {
959
1003
  const runId = requireArg(args.runId, "run id");
960
- const run = loadRun(args, runId);
961
- // Read BOTH the MCP camelCase keys and the CLI kebab-case flags: the CLI
962
- // parser emits `--required-approvals` as `required-approvals`, not
963
- // `requiredApprovals`, so without the fallback every flag was silently
964
- // dropped and the CLI wrote the default policy (caught by parity-check's
965
- // cw --json vs cw_review_policy payload probe).
966
- const requiredApprovals = args.requiredApprovals ?? args["required-approvals"];
967
- const authorizedRoles = args.authorizedRoles ?? args["authorized-roles"];
968
- const allowSelfApproval = args.allowSelfApproval ?? args["allow-self-approval"];
969
- const requireAttestedActor = args.requireAttestedActor ?? args["require-attested-actor"];
970
- const appliesTo = args.appliesTo ?? args["applies-to"];
971
- const policy = collab.setReviewPolicy(run, { requiredApprovals: numberArg(requiredApprovals), authorizedRoles: arrayArg(authorizedRoles).length ? arrayArg(authorizedRoles) : optionalString(authorizedRoles), allowSelfApproval: allowSelfApproval === undefined ? undefined : boolArg(allowSelfApproval), requireAttestedActor: requireAttestedActor === undefined ? undefined : boolArg(requireAttestedActor), appliesTo: arrayArg(appliesTo).length ? arrayArg(appliesTo) : optionalString(appliesTo) });
972
- return policy;
1004
+ return mutateRun(args, runId, (run) => {
1005
+ // Read BOTH the MCP camelCase keys and the CLI kebab-case flags: the CLI
1006
+ // parser emits `--required-approvals` as `required-approvals`, not
1007
+ // `requiredApprovals`, so without the fallback every flag was silently
1008
+ // dropped and the CLI wrote the default policy (caught by parity-check's
1009
+ // cw --json vs cw_review_policy payload probe).
1010
+ const requiredApprovals = args.requiredApprovals ?? args["required-approvals"];
1011
+ const authorizedRoles = args.authorizedRoles ?? args["authorized-roles"];
1012
+ const allowSelfApproval = args.allowSelfApproval ?? args["allow-self-approval"];
1013
+ const requireAttestedActor = args.requireAttestedActor ?? args["require-attested-actor"];
1014
+ const appliesTo = args.appliesTo ?? args["applies-to"];
1015
+ return collab.setReviewPolicy(run, { requiredApprovals: numberArg(requiredApprovals), authorizedRoles: arrayArg(authorizedRoles).length ? arrayArg(authorizedRoles) : optionalString(authorizedRoles), allowSelfApproval: allowSelfApproval === undefined ? undefined : boolArg(allowSelfApproval), requireAttestedActor: requireAttestedActor === undefined ? undefined : boolArg(requireAttestedActor), appliesTo: arrayArg(appliesTo).length ? arrayArg(appliesTo) : optionalString(appliesTo) });
1016
+ });
973
1017
  }
974
1018
  // ---------------------------------------------------------------------------
975
1019
  // Eval replay