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.
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/apps/architecture-review/app.json +1 -1
- package/apps/architecture-review-fast/app.json +1 -1
- package/apps/end-to-end-golden-path/app.json +1 -1
- package/apps/pr-review-fix-ci/app.json +1 -1
- package/apps/release-cut/app.json +1 -1
- package/apps/research-synthesis/app.json +1 -1
- package/dist/cli/dispatch.js +18 -86
- package/dist/cli/parseargv.js +5 -2
- package/dist/core/capability-data.js +270 -0
- package/dist/core/capability-table.js +16 -3504
- package/dist/core/format/help.js +28 -5
- package/dist/core/multi-agent/collaboration.js +3 -2
- package/dist/core/multi-agent/coordinator.js +4 -3
- package/dist/core/pipeline/commit-gate.js +2 -1
- package/dist/core/state/state-explosion/digest.js +3 -2
- package/dist/core/state/state-explosion/graph.js +9 -8
- package/dist/core/state/state-explosion/helpers.js +2 -1
- package/dist/core/trust/ledger.js +2 -1
- package/dist/core/types/execution-backend.js +18 -0
- package/dist/core/types/observability.js +7 -0
- package/dist/core/util/collate.js +23 -0
- package/dist/core/version.js +1 -1
- package/dist/shell/audit-cli.js +46 -2
- package/dist/shell/commit-summary.js +2 -1
- package/dist/shell/drive.js +26 -10
- package/dist/shell/evidence-reasoning.js +4 -3
- package/dist/shell/execution-backend/registry.js +2 -1
- package/dist/shell/execution-backend/types.js +0 -9
- package/dist/shell/fs-atomic.js +14 -1
- package/dist/shell/multi-agent-operator-ux.js +4 -3
- package/dist/shell/observability.js +12 -11
- package/dist/shell/onramp.js +17 -1
- package/dist/shell/operator-ux-text.js +2 -1
- package/dist/shell/operator-ux.js +7 -6
- package/dist/shell/pipeline-cli.js +82 -72
- package/dist/shell/reclamation-io.js +2 -1
- package/dist/shell/report.js +2 -1
- package/dist/shell/run-store.js +17 -0
- package/dist/shell/state-explosion-cli.js +2 -1
- package/dist/shell/topology-io.js +2 -1
- package/dist/shell/trust-audit.js +41 -2
- package/dist/shell/worker-cli.js +9 -1
- package/dist/shell/worker-isolation.js +34 -9
- package/dist/shell/workflow-app-loader.js +3 -2
- package/dist/wiring/capability-table/basics.js +66 -0
- package/dist/wiring/capability-table/exec-backend.js +162 -0
- package/dist/wiring/capability-table/index.js +42 -0
- package/dist/wiring/capability-table/multi-agent.js +623 -0
- package/dist/wiring/capability-table/parity.js +466 -0
- package/dist/wiring/capability-table/pipeline.js +280 -0
- package/dist/wiring/capability-table/registry-core.js +189 -0
- package/dist/wiring/capability-table/reporting.js +394 -0
- package/dist/wiring/capability-table/scheduling-registry.js +558 -0
- package/dist/wiring/capability-table/state.js +173 -0
- package/dist/wiring/capability-table/trust-ledger.js +134 -0
- package/dist/wiring/capability-table/workflow-apps.js +366 -0
- package/docs/agent-delegation-drive.7.md +2 -0
- package/docs/cli-mcp-parity.7.md +7 -2
- package/docs/contract-migration-tooling.7.md +2 -0
- package/docs/control-plane-scheduling.7.md +2 -0
- package/docs/durable-state-and-locking.7.md +22 -0
- package/docs/evidence-adoption-reasoning-chain.7.md +2 -0
- package/docs/execution-backends.7.md +2 -0
- package/docs/index.md +1 -0
- package/docs/multi-agent-cli-mcp-surface.7.md +2 -0
- package/docs/multi-agent-eval-replay-harness.7.md +2 -0
- package/docs/multi-agent-operator-ux.7.md +2 -0
- package/docs/node-snapshot-diff-replay.7.md +2 -0
- package/docs/observability-cost-accounting.7.md +2 -0
- package/docs/project-index.md +25 -5
- package/docs/real-execution-backends.7.md +2 -0
- package/docs/release-and-migration.7.md +2 -0
- package/docs/release-tooling.7.md +2 -0
- package/docs/run-registry-control-plane.7.md +2 -0
- package/docs/run-retention-reclamation.7.md +2 -0
- package/docs/security-trust-hardening.7.md +30 -0
- package/docs/state-explosion-management.7.md +2 -0
- package/docs/team-collaboration.7.md +2 -0
- package/docs/trust-audit-anchor.7.md +69 -0
- package/docs/web-desktop-workbench.7.md +2 -0
- package/manifest/plugin.manifest.json +1 -1
- package/package.json +3 -1
- package/scripts/bump-version.js +24 -2
- package/scripts/canonical-apps.js +4 -4
- package/scripts/dogfood-release.js +1 -1
- package/scripts/golden-path.js +4 -4
- package/scripts/purity-baseline.json +68 -0
- package/scripts/purity-gate.js +239 -0
- package/scripts/release-check.js +8 -1
- package/scripts/version-sync-check.js +33 -12
- 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
|
+
}
|