cool-workflow 0.2.1 → 0.2.2

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 (93) 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 +18 -86
  10. package/dist/cli/parseargv.js +5 -2
  11. package/dist/core/capability-data.js +270 -0
  12. package/dist/core/capability-table.js +16 -3504
  13. package/dist/core/format/help.js +28 -5
  14. package/dist/core/multi-agent/collaboration.js +3 -2
  15. package/dist/core/multi-agent/coordinator.js +4 -3
  16. package/dist/core/pipeline/commit-gate.js +2 -1
  17. package/dist/core/state/state-explosion/digest.js +3 -2
  18. package/dist/core/state/state-explosion/graph.js +9 -8
  19. package/dist/core/state/state-explosion/helpers.js +2 -1
  20. package/dist/core/trust/ledger.js +2 -1
  21. package/dist/core/types/execution-backend.js +18 -0
  22. package/dist/core/types/observability.js +7 -0
  23. package/dist/core/util/collate.js +23 -0
  24. package/dist/core/version.js +1 -1
  25. package/dist/shell/audit-cli.js +46 -2
  26. package/dist/shell/commit-summary.js +2 -1
  27. package/dist/shell/drive.js +26 -10
  28. package/dist/shell/evidence-reasoning.js +4 -3
  29. package/dist/shell/execution-backend/registry.js +2 -1
  30. package/dist/shell/execution-backend/types.js +0 -9
  31. package/dist/shell/fs-atomic.js +14 -1
  32. package/dist/shell/multi-agent-operator-ux.js +4 -3
  33. package/dist/shell/observability.js +12 -11
  34. package/dist/shell/onramp.js +17 -1
  35. package/dist/shell/operator-ux-text.js +2 -1
  36. package/dist/shell/operator-ux.js +7 -6
  37. package/dist/shell/pipeline-cli.js +82 -72
  38. package/dist/shell/reclamation-io.js +2 -1
  39. package/dist/shell/report.js +2 -1
  40. package/dist/shell/run-store.js +17 -0
  41. package/dist/shell/state-explosion-cli.js +2 -1
  42. package/dist/shell/topology-io.js +2 -1
  43. package/dist/shell/trust-audit.js +41 -2
  44. package/dist/shell/worker-cli.js +9 -1
  45. package/dist/shell/worker-isolation.js +34 -9
  46. package/dist/shell/workflow-app-loader.js +3 -2
  47. package/dist/wiring/capability-table/basics.js +66 -0
  48. package/dist/wiring/capability-table/exec-backend.js +162 -0
  49. package/dist/wiring/capability-table/index.js +42 -0
  50. package/dist/wiring/capability-table/multi-agent.js +623 -0
  51. package/dist/wiring/capability-table/parity.js +466 -0
  52. package/dist/wiring/capability-table/pipeline.js +280 -0
  53. package/dist/wiring/capability-table/registry-core.js +189 -0
  54. package/dist/wiring/capability-table/reporting.js +394 -0
  55. package/dist/wiring/capability-table/scheduling-registry.js +558 -0
  56. package/dist/wiring/capability-table/state.js +173 -0
  57. package/dist/wiring/capability-table/trust-ledger.js +134 -0
  58. package/dist/wiring/capability-table/workflow-apps.js +366 -0
  59. package/docs/agent-delegation-drive.7.md +2 -0
  60. package/docs/cli-mcp-parity.7.md +7 -2
  61. package/docs/contract-migration-tooling.7.md +2 -0
  62. package/docs/control-plane-scheduling.7.md +2 -0
  63. package/docs/durable-state-and-locking.7.md +22 -0
  64. package/docs/evidence-adoption-reasoning-chain.7.md +2 -0
  65. package/docs/execution-backends.7.md +2 -0
  66. package/docs/index.md +1 -0
  67. package/docs/multi-agent-cli-mcp-surface.7.md +2 -0
  68. package/docs/multi-agent-eval-replay-harness.7.md +2 -0
  69. package/docs/multi-agent-operator-ux.7.md +2 -0
  70. package/docs/node-snapshot-diff-replay.7.md +2 -0
  71. package/docs/observability-cost-accounting.7.md +2 -0
  72. package/docs/project-index.md +25 -5
  73. package/docs/real-execution-backends.7.md +2 -0
  74. package/docs/release-and-migration.7.md +2 -0
  75. package/docs/release-tooling.7.md +2 -0
  76. package/docs/run-registry-control-plane.7.md +2 -0
  77. package/docs/run-retention-reclamation.7.md +2 -0
  78. package/docs/security-trust-hardening.7.md +30 -0
  79. package/docs/state-explosion-management.7.md +2 -0
  80. package/docs/team-collaboration.7.md +2 -0
  81. package/docs/trust-audit-anchor.7.md +69 -0
  82. package/docs/web-desktop-workbench.7.md +2 -0
  83. package/manifest/plugin.manifest.json +1 -1
  84. package/package.json +3 -1
  85. package/scripts/bump-version.js +24 -2
  86. package/scripts/canonical-apps.js +4 -4
  87. package/scripts/dogfood-release.js +1 -1
  88. package/scripts/golden-path.js +4 -4
  89. package/scripts/purity-baseline.json +68 -0
  90. package/scripts/purity-gate.js +239 -0
  91. package/scripts/release-check.js +8 -1
  92. package/scripts/version-sync-check.js +33 -12
  93. package/workflows/README.md +19 -0
@@ -0,0 +1,466 @@
1
+ "use strict";
2
+ // wiring/capability-table/parity.ts — CLI <-> MCP parity planning + report.
3
+ // Split out of core/capability-table.ts, byte-for-byte (extracted with
4
+ // sed, not retyped).
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.CAPABILITY_REGISTRY = void 0;
7
+ exports.declaredMcpToolsList = declaredMcpToolsList;
8
+ exports.mcpRequiredArgsForTool = mcpRequiredArgsForTool;
9
+ exports.declaredCliTokens = declaredCliTokens;
10
+ exports.declaredCliHelpTokens = declaredCliHelpTokens;
11
+ exports.requiresReason = requiresReason;
12
+ exports.isPayloadProbeOptOut = isPayloadProbeOptOut;
13
+ exports.payloadIdenticalCapabilities = payloadIdenticalCapabilities;
14
+ exports.payloadProbeTargets = payloadProbeTargets;
15
+ exports.deferredPayloadProbeCapabilities = deferredPayloadProbeCapabilities;
16
+ exports.buildPayloadProbePlan = buildPayloadProbePlan;
17
+ exports.payloadProbePlan = payloadProbePlan;
18
+ exports.buildParityReport = buildParityReport;
19
+ const registry_core_1 = require("./registry-core");
20
+ // CLI <-> MCP parity planning + report. Ported from the old flat build's
21
+ // src/capability-registry.ts (its single source of parity data) onto this
22
+ // table's row shape. Same rule as that file's header: a capability marked
23
+ // `payloadIdentical` (the default for `surface: "both"`) MUST return a
24
+ // byte-for-byte equal JSON payload from `cw <cmd> --json` and from the
25
+ // `cw_<tool>` MCP result (whitespace aside); any divergence is drift. A
26
+ // capability reachable on one surface but absent on the other, or an
27
+ // undeclared payload divergence, is a release-blocking fail-closed error —
28
+ // see scripts/parity-check.js.
29
+ //
30
+ // `CAPABILITY_REGISTRY` is an alias so callers written against the old
31
+ // name (and the two smokes that import this module) find the same array
32
+ // under either name; `REGISTRY` stays the primary export other v2 modules
33
+ // already use.
34
+ exports.CAPABILITY_REGISTRY = registry_core_1.REGISTRY;
35
+ /** Read-only, run-less global reads: safe with just `cwd`. */
36
+ const GLOBAL_PAYLOAD_PROBE_CAPABILITIES = [
37
+ "list",
38
+ "app.list",
39
+ "topology.list",
40
+ "sandbox.list",
41
+ "backend.list",
42
+ "backend.agent.config.show",
43
+ "metrics.summary",
44
+ ];
45
+ /** Read-only reads that need only a planned run id. */
46
+ const RUN_PAYLOAD_PROBE_CAPABILITIES = [
47
+ "status",
48
+ "operator.status",
49
+ "operator.report",
50
+ "graph",
51
+ "report",
52
+ "next",
53
+ "state.check",
54
+ "contract.show",
55
+ "node.list",
56
+ "node.graph",
57
+ "worker.summary",
58
+ "candidate.summary",
59
+ "feedback.summary",
60
+ "commit.summary",
61
+ "audit.summary",
62
+ "audit.head",
63
+ "multi-agent.summary",
64
+ "workbench.view",
65
+ "metrics.show",
66
+ "review.status",
67
+ "comment.list",
68
+ "run.drive",
69
+ "gc.plan",
70
+ "gc.verify",
71
+ ];
72
+ /** Capabilities that need bespoke scenario setup (extra args, seeded state,
73
+ * a dispatched worker, ...) beyond a bare `cwd`/`runId` — see
74
+ * scripts/parity-check.js's `prepareScenarioCli`/`prepareScenarioMcp` and
75
+ * `runScenarioCli`/`runScenarioMcp` for the setup + invocation each one
76
+ * actually runs. */
77
+ const SCENARIO_PAYLOAD_PROBE_CAPABILITIES = [
78
+ "plan",
79
+ "app.show",
80
+ "app.validate",
81
+ "app.package",
82
+ "topology.show",
83
+ "topology.validate",
84
+ "topology.apply",
85
+ "topology.summary",
86
+ "topology.graph",
87
+ "summary.refresh",
88
+ "summary.show",
89
+ "sandbox.show",
90
+ "sandbox.validate",
91
+ "sandbox.choose",
92
+ "sandbox.resolve",
93
+ "approve",
94
+ "reject",
95
+ "comment.add",
96
+ "handoff",
97
+ "review.policy",
98
+ "worker.list",
99
+ "worker.show",
100
+ "worker.manifest",
101
+ "worker.output",
102
+ "worker.fail",
103
+ "worker.validate",
104
+ "candidate.list",
105
+ "candidate.show",
106
+ "candidate.register",
107
+ "candidate.score",
108
+ "candidate.rank",
109
+ "candidate.select",
110
+ "candidate.reject",
111
+ "feedback.list",
112
+ "feedback.show",
113
+ "feedback.collect",
114
+ "feedback.task",
115
+ "feedback.resolve",
116
+ "node.show",
117
+ "node.snapshot",
118
+ "node.diff",
119
+ "node.replay",
120
+ "node.replay.verify",
121
+ ];
122
+ /** Payload-identical, both-surface, dual-bound capabilities that are not
123
+ * yet safe for the deterministic bootstrap parity probe — each needs
124
+ * extra target ids/files, mutates durable state, depends on external
125
+ * state, or needs a dedicated fixture beyond cwd/runId. Every entry here
126
+ * must actually be a dual-bound (`cli` + `mcp`) row in `REGISTRY` without
127
+ * a declared opt-out, or it never reaches the candidate set this list is
128
+ * deferring — `buildPayloadProbePlan`'s `invalidClassifications` fails
129
+ * closed on a stale entry left behind after a row later grows a real
130
+ * probe target or an explicit opt-out. */
131
+ const PAYLOAD_PROBE_DEFERRED_GROUPS = [
132
+ {
133
+ reason: "Not safe for the deterministic bootstrap parity probe yet: this capability needs extra target ids/files, mutates durable state, depends on external state, or needs a dedicated fixture beyond cwd/runId.",
134
+ capabilities: [
135
+ "dispatch",
136
+ "result",
137
+ "app.init",
138
+ "migration.list",
139
+ "migration.check",
140
+ "migration.prove",
141
+ "multi-agent.run",
142
+ "multi-agent.status",
143
+ "multi-agent.step",
144
+ "multi-agent.blackboard",
145
+ "multi-agent.score",
146
+ "multi-agent.select",
147
+ "multi-agent.summarize",
148
+ "multi-agent.graph",
149
+ "multi-agent.dependencies",
150
+ "multi-agent.failures",
151
+ "multi-agent.evidence",
152
+ "multi-agent.reasoning",
153
+ "multi-agent.run.create",
154
+ "multi-agent.run.transition",
155
+ "multi-agent.run.show",
156
+ "multi-agent.group.create",
157
+ "multi-agent.membership.create",
158
+ "multi-agent.fanout.create",
159
+ "multi-agent.fanin.collect",
160
+ "eval.snapshot",
161
+ "eval.replay",
162
+ "eval.compare",
163
+ "eval.score",
164
+ "eval.gate",
165
+ "eval.report",
166
+ "blackboard.summary",
167
+ "blackboard.summarize",
168
+ "blackboard.graph",
169
+ "blackboard.resolve",
170
+ "blackboard.topic.create",
171
+ "blackboard.message.post",
172
+ "blackboard.message.list",
173
+ "blackboard.context.put",
174
+ "blackboard.artifact.add",
175
+ "blackboard.artifact.list",
176
+ "blackboard.snapshot",
177
+ "coordinator.summary",
178
+ "coordinator.decision",
179
+ "audit.verify",
180
+ "audit.worker",
181
+ "audit.provenance",
182
+ "audit.multi-agent",
183
+ "audit.policy",
184
+ "audit.role",
185
+ "audit.blackboard",
186
+ "audit.judge",
187
+ "audit.attest",
188
+ "audit.decision",
189
+ "backend.show",
190
+ "backend.probe",
191
+ "run.search",
192
+ "run.list",
193
+ "run.show",
194
+ "run.resume",
195
+ "run.archive",
196
+ "run.rerun",
197
+ "report.verify-bundle",
198
+ "report.bundle",
199
+ "telemetry.verify",
200
+ "history",
201
+ ],
202
+ },
203
+ {
204
+ // Phase B: the CLI bindings just layered onto these previously
205
+ // MCP-only rows make each a real both-surface dual-bound capability, so
206
+ // the payload probe now sees them. Each needs a seeded fixture beyond a
207
+ // bare cwd/runId — a scheduled task / routine trigger / durable queue
208
+ // entry / lease / portable archive on disk, a target entity id
209
+ // (role/group/membership/fanout/fanin/schedule/lease id), a workflow-app
210
+ // id to scaffold or drive, or a registry index to refresh — so each is
211
+ // deferred until a bootstrap fixture is added, exactly like the
212
+ // scenario/deferred split the older batches use. `init` also folds into
213
+ // app.init on both surfaces (scaffold), so it defers with the app family.
214
+ reason: "Not safe for the deterministic bootstrap parity probe yet: this capability needs a seeded fixture beyond cwd/runId (a scheduled task / routine trigger / queue entry / lease / portable archive on disk, a target entity id, or a workflow-app id to scaffold or drive). Both surfaces route through the same shell fn; each defers until a bootstrap fixture seeds its state.",
215
+ capabilities: [
216
+ "app.run",
217
+ "init",
218
+ "registry.refresh",
219
+ "registry.show",
220
+ "queue.add",
221
+ "queue.list",
222
+ "queue.drain",
223
+ "queue.show",
224
+ "clones.list",
225
+ "orphans.list",
226
+ "schedule.create",
227
+ "schedule.list",
228
+ "schedule.delete",
229
+ "schedule.due",
230
+ "schedule.complete",
231
+ "schedule.pause",
232
+ "schedule.resume",
233
+ "schedule.run-now",
234
+ "schedule.history",
235
+ "routine.create",
236
+ "routine.list",
237
+ "routine.delete",
238
+ "routine.fire",
239
+ "routine.events",
240
+ "sched.plan",
241
+ "sched.lease",
242
+ "sched.release",
243
+ "sched.complete",
244
+ "sched.reclaim",
245
+ "sched.reset",
246
+ "sched.policy.show",
247
+ "sched.policy.set",
248
+ "run.export",
249
+ "run.import",
250
+ "run.verify-import",
251
+ "run.inspect-archive",
252
+ "run.restore",
253
+ "multi-agent.reasoning.refresh",
254
+ "multi-agent.graph.compact",
255
+ "multi-agent.role.create",
256
+ "multi-agent.role.show",
257
+ "multi-agent.group.show",
258
+ "multi-agent.membership.show",
259
+ "multi-agent.fanout.show",
260
+ "multi-agent.fanin.show",
261
+ ],
262
+ },
263
+ ];
264
+ /** The MCP tool names this registry declares. */
265
+ function declaredMcpToolsList() {
266
+ return (0, registry_core_1.declaredMcpTools)();
267
+ }
268
+ /** Required MCP argument groups for a registry-declared tool. */
269
+ function mcpRequiredArgsForTool(tool) {
270
+ return (0, registry_core_1.findCapabilityByMcpTool)(tool)?.mcp?.requiredArgs ?? [];
271
+ }
272
+ /** The CLI `case` tokens this registry declares (deduped). */
273
+ function declaredCliTokens() {
274
+ const tokens = new Set();
275
+ for (const cap of registry_core_1.REGISTRY) {
276
+ if (!cap.cli)
277
+ continue;
278
+ for (const token of cap.cli.caseTokens ?? cap.cli.path)
279
+ tokens.add(token);
280
+ }
281
+ return [...tokens].sort();
282
+ }
283
+ /** The top-level CLI commands that should be visible in `cw help`.
284
+ * Subcommands are collapsed to their first token; alias tokens (e.g.
285
+ * `audit-run`) stay visible alongside the verb they alias. */
286
+ function declaredCliHelpTokens() {
287
+ const tokens = new Set();
288
+ for (const cap of registry_core_1.REGISTRY) {
289
+ if (!cap.cli)
290
+ continue;
291
+ const subcommandTokens = new Set(cap.cli.path.slice(1));
292
+ tokens.add(cap.cli.path[0]);
293
+ for (const token of cap.cli.caseTokens || []) {
294
+ if (!subcommandTokens.has(token))
295
+ tokens.add(token);
296
+ }
297
+ }
298
+ tokens.delete("help");
299
+ // `init` is a help-index-only token: v2 folds the standalone `init`
300
+ // capability into `app.init` (its cli.path is ["init"] only so the
301
+ // dispatcher can still run `cw init`, but `cw help` lists it in the
302
+ // frozen "More commands" index line, never as its own per-command help
303
+ // row). Mirrors the parity smoke's HELP_INDEX_ONLY_TOKENS set so the
304
+ // help-token parity stays balanced.
305
+ tokens.delete("init");
306
+ // `search` is likewise help-index-only: its row is hiddenFromHelp (it
307
+ // never had its own `cw help search` row — only the frozen "More
308
+ // commands" index line, which this function does not build), and unlike
309
+ // a family such as `clones` it has no visible sibling row sharing the
310
+ // "search" first token to contribute it independently. Mirrors the
311
+ // parity smoke's HELP_INDEX_ONLY_TOKENS set so the help-token parity
312
+ // stays balanced.
313
+ tokens.delete("search");
314
+ return [...tokens].sort();
315
+ }
316
+ /** Whether a row MUST carry a reason (surface-specific or payload-divergent). */
317
+ function requiresReason(cap) {
318
+ if (cap.surface !== "both")
319
+ return true;
320
+ if (cap.payloadIdentical === false)
321
+ return true;
322
+ return false;
323
+ }
324
+ /**
325
+ * Whether a `surface:"both"` capability is DOCUMENTED out of the payload-identity
326
+ * probe. The probe defaults capabilities IN (every both-surface, dual-bound verb —
327
+ * including write/complex-arg verbs) and requires an EXPLICIT, REASONED opt-out to
328
+ * fall out of scope. A capability escapes the probe only when it carries BOTH
329
+ * `payloadIdentical: false` AND a non-empty `reason`. A bare `payloadIdentical:
330
+ * false` with no recorded reason does NOT silently escape — it stays in the probe
331
+ * set so the undocumented divergence trips the gate (FAIL CLOSED).
332
+ */
333
+ function isPayloadProbeOptOut(cap) {
334
+ return cap.payloadIdentical === false && !!(cap.reason && cap.reason.trim());
335
+ }
336
+ /** Rows for the payload-identity probe. Defaults to EVERY both-surface,
337
+ * dual-bound capability (read OR write); a row is excluded only by a
338
+ * documented opt-out (`payloadIdentical: false` + a non-empty `reason`) —
339
+ * see `isPayloadProbeOptOut`. Fail-closed: an undocumented `payloadIdentical:
340
+ * false` stays in scope so its divergence is caught, not silently excused. */
341
+ function payloadIdenticalCapabilities() {
342
+ return registry_core_1.REGISTRY.filter((cap) => cap.surface === "both" && cap.cli && cap.mcp && !isPayloadProbeOptOut(cap));
343
+ }
344
+ function payloadProbeTargets() {
345
+ return [
346
+ ...GLOBAL_PAYLOAD_PROBE_CAPABILITIES.map((capability) => ({ capability, kind: "global" })),
347
+ ...RUN_PAYLOAD_PROBE_CAPABILITIES.map((capability) => ({ capability, kind: "run" })),
348
+ ...SCENARIO_PAYLOAD_PROBE_CAPABILITIES.map((capability) => ({ capability, kind: "scenario" })),
349
+ ];
350
+ }
351
+ function deferredPayloadProbeCapabilities() {
352
+ return PAYLOAD_PROBE_DEFERRED_GROUPS.flatMap((group) => group.capabilities.map((capability) => ({ capability, reason: group.reason })));
353
+ }
354
+ function buildPayloadProbePlan(targets, deferred) {
355
+ const candidateIds = new Set(payloadIdenticalCapabilities().map((cap) => cap.capability));
356
+ const counts = new Map();
357
+ const classified = [...targets.map((entry) => entry.capability), ...deferred.map((entry) => entry.capability)];
358
+ for (const capability of classified)
359
+ counts.set(capability, (counts.get(capability) || 0) + 1);
360
+ const classifiedIds = new Set(classified);
361
+ return {
362
+ targets,
363
+ deferred,
364
+ unclassified: [...candidateIds].filter((capability) => !classifiedIds.has(capability)).sort(),
365
+ duplicateClassifications: [...counts.entries()]
366
+ .filter(([, count]) => count > 1)
367
+ .map(([capability]) => capability)
368
+ .sort(),
369
+ invalidClassifications: [...classifiedIds].filter((capability) => !candidateIds.has(capability)).sort(),
370
+ };
371
+ }
372
+ function payloadProbePlan() {
373
+ return buildPayloadProbePlan(payloadProbeTargets(), deferredPayloadProbeCapabilities());
374
+ }
375
+ function lintRegistry() {
376
+ const issues = [];
377
+ const seenCaps = new Set();
378
+ const seenTools = new Set();
379
+ for (const cap of registry_core_1.REGISTRY) {
380
+ if (seenCaps.has(cap.capability))
381
+ issues.push(`duplicate capability id: ${cap.capability}`);
382
+ seenCaps.add(cap.capability);
383
+ if (cap.mcp) {
384
+ if (seenTools.has(cap.mcp.tool))
385
+ issues.push(`duplicate MCP tool: ${cap.mcp.tool}`);
386
+ seenTools.add(cap.mcp.tool);
387
+ }
388
+ // NOTE (v2 build-order difference from the old flat registry): the old
389
+ // build's registry was written all at once, so a "both" row ALWAYS had
390
+ // both bindings the moment it was declared. This table is built up
391
+ // MILESTONE BY MILESTONE (this file's header note): `REGISTRY` starts
392
+ // from the full, literal 196-tool `mcp` surface (SPEC/mcp.md) and each
393
+ // milestone LAYERS a `cli` binding onto the rows it wires next — so a
394
+ // "both" row with an `mcp` binding but no `cli` binding YET is the
395
+ // expected, honest mid-rollout state, not a lint error. `cli` bindings
396
+ // are added without ever touching `surface`, so the lint only fails
397
+ // closed on what is actually impossible: a `mcp` binding must always
398
+ // exist for "both" (every row starts from `MCP_TOOL_DATA`), and
399
+ // `cli-only`/`mcp-only` rows must carry exactly the one binding their
400
+ // name promises.
401
+ if (cap.surface === "both" && !cap.mcp) {
402
+ issues.push(`${cap.capability}: surface "both" requires an mcp binding`);
403
+ }
404
+ if (cap.surface === "cli-only" && (cap.mcp || !cap.cli)) {
405
+ issues.push(`${cap.capability}: surface "cli-only" requires a cli binding and no mcp binding`);
406
+ }
407
+ if (cap.surface === "mcp-only" && (cap.cli || !cap.mcp)) {
408
+ issues.push(`${cap.capability}: surface "mcp-only" requires an mcp binding and no cli binding`);
409
+ }
410
+ }
411
+ return issues;
412
+ }
413
+ /**
414
+ * Compare the declared registry against the ACTUAL surfaces and report every
415
+ * fail-closed gap. `mcpTools` is the live `tools/list` result; `cliTokens` is the
416
+ * set of `case "<token>"` strings parsed from the CLI source.
417
+ */
418
+ function buildParityReport(input) {
419
+ const declaredTools = new Set((0, registry_core_1.declaredMcpTools)());
420
+ const actualTools = new Set(input.mcpTools);
421
+ const declaredTokens = new Set(declaredCliTokens());
422
+ const actualTokens = new Set(input.cliTokens);
423
+ const declaredHelpTokens = new Set(declaredCliHelpTokens());
424
+ const actualHelpTokens = new Set(input.helpTokens || []);
425
+ const missingMcpTools = [...declaredTools].filter((tool) => !actualTools.has(tool)).sort();
426
+ const undeclaredMcpTools = [...actualTools].filter((tool) => !declaredTools.has(tool)).sort();
427
+ const missingCliTokens = [...declaredTokens].filter((token) => !actualTokens.has(token)).sort();
428
+ const undeclaredCliTokens = [...actualTokens].filter((token) => !declaredTokens.has(token)).sort();
429
+ const helpMissingCliTokens = input.helpTokens
430
+ ? [...declaredHelpTokens].filter((token) => !actualHelpTokens.has(token)).sort()
431
+ : [];
432
+ const helpUndeclaredCliTokens = input.helpTokens
433
+ ? [...actualHelpTokens].filter((token) => !declaredHelpTokens.has(token)).sort()
434
+ : [];
435
+ const reasonlessExceptions = registry_core_1.REGISTRY.filter((cap) => requiresReason(cap) && !(cap.reason && cap.reason.trim()))
436
+ .map((cap) => cap.capability)
437
+ .sort();
438
+ const payloadPlan = payloadProbePlan();
439
+ const registryLint = lintRegistry();
440
+ const ok = missingMcpTools.length === 0 &&
441
+ undeclaredMcpTools.length === 0 &&
442
+ missingCliTokens.length === 0 &&
443
+ undeclaredCliTokens.length === 0 &&
444
+ helpMissingCliTokens.length === 0 &&
445
+ helpUndeclaredCliTokens.length === 0 &&
446
+ reasonlessExceptions.length === 0 &&
447
+ payloadPlan.unclassified.length === 0 &&
448
+ payloadPlan.duplicateClassifications.length === 0 &&
449
+ payloadPlan.invalidClassifications.length === 0 &&
450
+ registryLint.length === 0;
451
+ return {
452
+ ok,
453
+ registrySize: registry_core_1.REGISTRY.length,
454
+ missingMcpTools,
455
+ undeclaredMcpTools,
456
+ missingCliTokens,
457
+ undeclaredCliTokens,
458
+ helpMissingCliTokens,
459
+ helpUndeclaredCliTokens,
460
+ reasonlessExceptions,
461
+ payloadProbeUnclassified: payloadPlan.unclassified,
462
+ payloadProbeDuplicateClassifications: payloadPlan.duplicateClassifications,
463
+ payloadProbeInvalidClassifications: payloadPlan.invalidClassifications,
464
+ registryLint,
465
+ };
466
+ }