@honua/mcp-server 0.1.10-beta.0 → 0.1.12

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 (34) hide show
  1. package/README.md +3 -0
  2. package/certification/setup-parity.md +79 -0
  3. package/dist/src/certification/setup-parity.d.ts +86 -0
  4. package/dist/src/certification/setup-parity.d.ts.map +1 -0
  5. package/dist/src/certification/setup-parity.js +188 -0
  6. package/dist/src/eval/cli.js +2 -2
  7. package/dist/src/eval/corpus.d.ts.map +1 -1
  8. package/dist/src/eval/corpus.js +6 -5
  9. package/dist/src/eval/grade.d.ts.map +1 -1
  10. package/dist/src/eval/grade.js +25 -0
  11. package/dist/src/eval/report.d.ts +9 -0
  12. package/dist/src/eval/report.d.ts.map +1 -1
  13. package/dist/src/eval/report.js +15 -5
  14. package/dist/src/eval/runner.d.ts.map +1 -1
  15. package/dist/src/eval/runner.js +16 -1
  16. package/dist/src/eval/types.d.ts +37 -2
  17. package/dist/src/eval/types.d.ts.map +1 -1
  18. package/dist/src/eval/workflow-corpus.d.ts +9 -0
  19. package/dist/src/eval/workflow-corpus.d.ts.map +1 -0
  20. package/dist/src/eval/workflow-corpus.js +306 -0
  21. package/dist/src/proxy.d.ts.map +1 -1
  22. package/dist/src/proxy.js +7 -1
  23. package/dist/src/release/zero-to-map-cli.d.ts.map +1 -1
  24. package/dist/src/release/zero-to-map-cli.js +7 -10
  25. package/dist/src/release/zero-to-map.d.ts +36 -69
  26. package/dist/src/release/zero-to-map.d.ts.map +1 -1
  27. package/dist/src/release/zero-to-map.js +102 -178
  28. package/package.json +4 -2
  29. package/release/zero-to-map/README.md +61 -83
  30. package/release/zero-to-map/configs/claude-code.mcp.json +1 -1
  31. package/release/zero-to-map/configs/claude-desktop.json +1 -1
  32. package/release/zero-to-map/configs/cursor.json +1 -1
  33. package/release/zero-to-map/journey.v1.json +163 -751
  34. package/release/zero-to-map/contracts/esri-gp-mcp.v1.json +0 -73
package/README.md CHANGED
@@ -8,6 +8,9 @@ bound to their own platform; this one is bound to none.
8
8
 
9
9
  **Release status: beta.** Tool contracts are certified against live and fixture
10
10
  targets on every release; remaining pre-1.0 work is hardening, not surface change.
11
+ "Beta" is a maturity label, not part of the version: releases are stable `0.x`
12
+ versions, and the `@honua/sdk-js` peer is a caret range (for example `^0.1.12`),
13
+ so a later `@honua/sdk-js` patch installs beside this package without a republish.
11
14
 
12
15
  **Scores are published, not claimed.** How well different client models actually
13
16
  drive this surface — and where they fail — is in the
@@ -0,0 +1,79 @@
1
+ # Bounded setup catalog qualification
2
+
3
+ The 2026.1 setup gate uses the candidate's server-authored workflow view. It
4
+ does not require the legacy full Admin, analysis and Esri GP roster. Run the
5
+ catalog check from an installed, integrity-verified MCP package:
6
+
7
+ ```sh
8
+ node node_modules/@honua/mcp-server/dist/src/certification/setup-parity.js \
9
+ reviewed-server-manifest.json <independently-pinned-manifest-sha256> \
10
+ setup-catalog-receipt.json
11
+ ```
12
+
13
+ From source, the equivalent command is `npm run certify:setup-parity -- ...`
14
+ in `mcp/`. Configure `HONUA_MCP_REMOTE_URL` and exactly one credential source:
15
+ `HONUA_MCP_AUTH_TOKEN`, `HONUA_ADMIN_KEY`, or `HONUA_API_KEY`. Credentials must
16
+ not appear in arguments, URLs, the manifest or receipts. The existing proxy
17
+ enforces HTTPS for credential-bearing endpoints, with the local loopback
18
+ exception used by the Docker installer.
19
+
20
+ The manifest is a reviewed capture from the pinned server under the intended
21
+ effective permissions. Keep its SHA-256 in the release lock/evidence index,
22
+ separate from the captured file. It has this structure:
23
+
24
+ ```json
25
+ {
26
+ "schemaVersion": "honua.setup-catalog-manifest/v1",
27
+ "candidateId": "manifest-sha256:<release-manifest-digest>",
28
+ "serverImage": "ghcr.io/honua-io/honua-server@sha256:<image-digest>",
29
+ "packages": [
30
+ { "name": "@honua/sdk-js", "version": "<exact-version>", "integrity": "sha512-<registry-integrity>" },
31
+ { "name": "@honua/mcp-server", "version": "<exact-version>", "integrity": "sha512-<registry-integrity>" }
32
+ ],
33
+ "serverInfo": { "name": "<initialize serverInfo.name>", "version": "<initialize serverInfo.version>" },
34
+ "view": "<server-published bounded view>",
35
+ "revision": "<tools/list _meta.revision>",
36
+ "revisionDigest": "<tools/list _meta.revisionDigest>",
37
+ "membershipDigest": "<tools/list _meta.membershipDigest>",
38
+ "descriptorDigest": "<tools/list _meta.descriptorDigest>",
39
+ "tools": ["<complete original tool objects from all pages, not names or abbreviated schemas>"]
40
+ }
41
+ ```
42
+
43
+ The placeholders above illustrate the shape and are deliberately invalid as
44
+ qualification input. Discover the view from the server's capabilities; do not
45
+ invent profile switches or assemble a replacement tool roster in the client.
46
+ Retain complete descriptions, input/output schemas, annotations and `_meta`.
47
+ The server's opaque digests are compared to the reviewed pin. The receipt also
48
+ computes independent normalized descriptor hashes for HTTP and stdio; these
49
+ use a different serialization from the server and have distinct field names.
50
+
51
+ The check drains all pages over direct HTTP and a real child-process stdio
52
+ proxy, carrying the selected view on every page. It fails on missing, extra,
53
+ duplicate or changed tools, missing/changed revision metadata, server identity
54
+ drift, cursor loops and proxy differences. Missing tools identify candidate
55
+ view/effective permissions as the diagnostic boundary. No total such as 441 is
56
+ used to decide success, and a complete but short catalog is a membership
57
+ failure rather than a guessed pagination fault.
58
+
59
+ The receipt's scope is `catalog-parity-only`. Its candidate image and package
60
+ integrities are **bindings from the reviewed manifest**, not a claim that this
61
+ command inspected the running image or verified installed package bytes.
62
+ The release producer must verify those bindings against its immutable install
63
+ receipt before consuming this result. This read-only check does not install a
64
+ server, mutate styles, prove `geometry.buffer` execution, validate effective
65
+ permissions, or satisfy all of #1401. Retain the separate clean-install,
66
+ readiness/auth, bounded task execution, get-style → apply-style-preset →
67
+ render-map → read-resource PNG, and versioned-guidance receipts for
68
+ honua-release#123/#129/#161. Existing `assertRenderedPng` checks reject empty
69
+ or flat renders; a catalog match alone cannot establish style success.
70
+
71
+ `honua admin qualify` joins this receipt to the local install pin, effective
72
+ grants, server identity, and the style/render PNG proof. It re-reads the
73
+ bounded view over HTTP, requires this receipt's HTTP and stdio descriptor
74
+ hashes to match, and still does not treat a catalog match as style success.
75
+
76
+ The older zero-to-map runner retains its broader workflow as an explicit
77
+ legacy integration exercise. It must not be used as the 2026.1 bounded catalog
78
+ gate, or its full Admin/analysis prerequisites would reintroduce deferred
79
+ scope into first-cut qualification.
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env node
2
+ import { type Tool } from "@modelcontextprotocol/sdk/types.js";
3
+ /** A reviewed capture from the pinned server, never a client-authored tool roster. */
4
+ export interface SetupCatalogManifest {
5
+ schemaVersion: "honua.setup-catalog-manifest/v1";
6
+ candidateId: string;
7
+ serverImage: string;
8
+ packages: {
9
+ name: string;
10
+ version: string;
11
+ integrity: string;
12
+ }[];
13
+ serverInfo: {
14
+ name: string;
15
+ version: string;
16
+ };
17
+ view: string;
18
+ revision: string;
19
+ revisionDigest: string;
20
+ membershipDigest: string;
21
+ descriptorDigest: string;
22
+ tools: Tool[];
23
+ }
24
+ export interface SetupCatalogSnapshot {
25
+ tools: Tool[];
26
+ metadata: Record<string, unknown>;
27
+ serverInfo: unknown;
28
+ }
29
+ export declare function parseSetupCatalogManifest(bytes: string, expectedSha256: string): SetupCatalogManifest;
30
+ /** Drain the selected view, retaining server revision metadata across every page. */
31
+ export declare function readSetupCatalog(client: {
32
+ listTools(request?: {
33
+ cursor?: string;
34
+ }): Promise<{
35
+ tools: Tool[];
36
+ nextCursor?: string;
37
+ _meta?: Record<string, unknown>;
38
+ }>;
39
+ getServerVersion(): unknown;
40
+ }, view: string): Promise<SetupCatalogSnapshot>;
41
+ /** Catalog evidence only: installation, authorization and style execution have separate receipts. */
42
+ export declare function certifySetupCatalogParity(manifest: SetupCatalogManifest, manifestSha256: string, direct: SetupCatalogSnapshot, proxied: SetupCatalogSnapshot): {
43
+ schemaVersion: "honua.setup-catalog-parity/v1";
44
+ scope: "catalog-parity-only";
45
+ candidateId: string;
46
+ serverImage: string;
47
+ packages: {
48
+ name: string;
49
+ version: string;
50
+ integrity: string;
51
+ }[];
52
+ manifestSha256: string;
53
+ view: string;
54
+ revision: string;
55
+ revisionDigest: string;
56
+ expectedTools: number;
57
+ directTools: number;
58
+ proxiedTools: number;
59
+ directDescriptorSha256: string;
60
+ proxiedDescriptorSha256: string;
61
+ pass: boolean;
62
+ differences: string[];
63
+ };
64
+ export declare function runSetupParityCertification(manifestPath: string, manifestSha256: string, outputPath: string, env?: NodeJS.ProcessEnv): Promise<{
65
+ schemaVersion: "honua.setup-catalog-parity/v1";
66
+ scope: "catalog-parity-only";
67
+ candidateId: string;
68
+ serverImage: string;
69
+ packages: {
70
+ name: string;
71
+ version: string;
72
+ integrity: string;
73
+ }[];
74
+ manifestSha256: string;
75
+ view: string;
76
+ revision: string;
77
+ revisionDigest: string;
78
+ expectedTools: number;
79
+ directTools: number;
80
+ proxiedTools: number;
81
+ directDescriptorSha256: string;
82
+ proxiedDescriptorSha256: string;
83
+ pass: boolean;
84
+ differences: string[];
85
+ }>;
86
+ //# sourceMappingURL=setup-parity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup-parity.d.ts","sourceRoot":"","sources":["../../../src/certification/setup-parity.ts"],"names":[],"mappings":";AAMA,OAAO,EAAE,KAAK,IAAI,EAAc,MAAM,oCAAoC,CAAC;AAK3E,sFAAsF;AACtF,MAAM,WAAW,oBAAoB;IACnC,aAAa,EAAE,iCAAiC,CAAC;IACjD,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACjE,UAAU,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,KAAK,EAAE,IAAI,EAAE,CAAC;CACf;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,UAAU,EAAE,OAAO,CAAC;CACrB;AAsBD,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,oBAAoB,CA+CrG;AAED,qFAAqF;AACrF,wBAAsB,gBAAgB,CACpC,MAAM,EAAE;IACN,SAAS,CAAC,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAChD,KAAK,EAAE,IAAI,EAAE,CAAC;QACd,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACjC,CAAC,CAAC;IACH,gBAAgB,IAAI,OAAO,CAAC;CAC7B,EACD,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,oBAAoB,CAAC,CAiB/B;AAED,qGAAqG;AACrG,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,oBAAoB,EAC9B,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,oBAAoB,EAC5B,OAAO,EAAE,oBAAoB;;;;;;;;;;;;;;;;;;;;;EAgD9B;AAED,wBAAsB,2BAA2B,CAC/C,YAAY,EAAE,MAAM,EACpB,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,EAClB,GAAG,GAAE,MAAM,CAAC,UAAwB;;;;;;;;;;;;;;;;;;;;;GA0BrC"}
@@ -0,0 +1,188 @@
1
+ #!/usr/bin/env node
2
+ import { createHash } from "node:crypto";
3
+ import { readFile, writeFile } from "node:fs/promises";
4
+ import { fileURLToPath } from "node:url";
5
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
6
+ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
7
+ import { ToolSchema } from "@modelcontextprotocol/sdk/types.js";
8
+ import { isMainEntrypoint } from "../entrypoint.js";
9
+ import { connectUpstream, resolveProxyOptions } from "../proxy.js";
10
+ import { listAllTools } from "./admin-parity.js";
11
+ function stableJson(value) {
12
+ if (Array.isArray(value))
13
+ return `[${value.map(stableJson).join(",")}]`;
14
+ if (value && typeof value === "object") {
15
+ return `{${Object.entries(value)
16
+ .filter(([, item]) => item !== undefined)
17
+ .sort(([a], [b]) => a.localeCompare(b))
18
+ .map(([key, item]) => `${JSON.stringify(key)}:${stableJson(item)}`)
19
+ .join(",")}}`;
20
+ }
21
+ return JSON.stringify(value) ?? "null";
22
+ }
23
+ function sha256(value) {
24
+ return createHash("sha256").update(value).digest("hex");
25
+ }
26
+ function record(value) {
27
+ return value !== null && typeof value === "object" && !Array.isArray(value);
28
+ }
29
+ export function parseSetupCatalogManifest(bytes, expectedSha256) {
30
+ if (!/^[a-f0-9]{64}$/.test(expectedSha256) || sha256(bytes) !== expectedSha256) {
31
+ throw new Error("Setup manifest SHA-256 does not match the independently pinned digest");
32
+ }
33
+ const value = JSON.parse(bytes);
34
+ if (!record(value) || value.schemaVersion !== "honua.setup-catalog-manifest/v1") {
35
+ throw new Error("Expected honua.setup-catalog-manifest/v1");
36
+ }
37
+ for (const key of ["candidateId", "view", "revision"]) {
38
+ if (typeof value[key] !== "string" || !value[key].trim())
39
+ throw new Error(`Manifest requires ${key}`);
40
+ }
41
+ if (value.view === "full")
42
+ throw new Error("Setup qualification requires a bounded server-authored view");
43
+ if (typeof value.serverImage !== "string" || !/^[^\s@]+@sha256:[a-f0-9]{64}$/.test(value.serverImage)) {
44
+ throw new Error("Manifest requires an immutable server image digest");
45
+ }
46
+ for (const key of ["revisionDigest", "membershipDigest", "descriptorDigest"]) {
47
+ if (typeof value[key] !== "string" || !/^[a-f0-9]{64}$/.test(value[key])) {
48
+ throw new Error(`Manifest requires server-authored ${key}`);
49
+ }
50
+ }
51
+ if (!record(value.serverInfo) ||
52
+ typeof value.serverInfo.name !== "string" ||
53
+ typeof value.serverInfo.version !== "string") {
54
+ throw new Error("Manifest requires the server initialize identity");
55
+ }
56
+ if (!Array.isArray(value.packages) ||
57
+ value.packages.length === 0 ||
58
+ value.packages.some((pkg) => !record(pkg) ||
59
+ typeof pkg.name !== "string" ||
60
+ !/^(?:@[a-z0-9._-]+\/)?[a-z0-9._-]+$/.test(pkg.name) ||
61
+ typeof pkg.version !== "string" ||
62
+ !/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(pkg.version) ||
63
+ typeof pkg.integrity !== "string" ||
64
+ !/^sha512-[A-Za-z0-9+/]{86}==$/.test(pkg.integrity)))
65
+ throw new Error("Manifest requires exact package versions and sha512 integrities");
66
+ if (!Array.isArray(value.tools) || value.tools.length === 0)
67
+ throw new Error("Manifest has no enabled tools");
68
+ for (const tool of value.tools)
69
+ ToolSchema.parse(tool);
70
+ const names = value.tools.map((tool) => tool.name);
71
+ if (new Set(names).size !== names.length)
72
+ throw new Error("Manifest repeats a tool name");
73
+ return value;
74
+ }
75
+ /** Drain the selected view, retaining server revision metadata across every page. */
76
+ export async function readSetupCatalog(client, view) {
77
+ let metadata;
78
+ const tools = await listAllTools({
79
+ async listTools(request) {
80
+ const page = await client.listTools(request);
81
+ if (!page._meta)
82
+ throw new Error("tools/list omitted the server-authored view metadata");
83
+ if (metadata && stableJson(metadata) !== stableJson(page._meta)) {
84
+ throw new Error("tools/list changed view metadata during pagination; repeat against a stable candidate");
85
+ }
86
+ metadata = page._meta;
87
+ return page;
88
+ },
89
+ }, { view });
90
+ return { tools, metadata: metadata ?? {}, serverInfo: client.getServerVersion() };
91
+ }
92
+ /** Catalog evidence only: installation, authorization and style execution have separate receipts. */
93
+ export function certifySetupCatalogParity(manifest, manifestSha256, direct, proxied) {
94
+ const differences = [];
95
+ const expected = new Map(manifest.tools.map((tool) => [tool.name, tool]));
96
+ for (const [transport, snapshot] of [
97
+ ["HTTP", direct],
98
+ ["stdio", proxied],
99
+ ]) {
100
+ if (stableJson(snapshot.serverInfo) !== stableJson(manifest.serverInfo))
101
+ differences.push(`${transport}: server identity differs from the pin`);
102
+ const names = snapshot.tools.map((tool) => tool.name);
103
+ if (new Set(names).size !== names.length)
104
+ differences.push(`${transport}: duplicate tool names`);
105
+ for (const key of ["view", "revision", "revisionDigest", "membershipDigest", "descriptorDigest"]) {
106
+ if (snapshot.metadata[key] !== manifest[key])
107
+ differences.push(`${transport}: ${key} differs from the pin`);
108
+ }
109
+ if (snapshot.metadata.toolCount !== manifest.tools.length)
110
+ differences.push(`${transport}: declared toolCount differs from the enabled manifest`);
111
+ const actual = new Map(snapshot.tools.map((tool) => [tool.name, tool]));
112
+ for (const name of expected.keys()) {
113
+ if (!actual.has(name))
114
+ differences.push(`${transport}: missing enabled tool ${name}; check candidate view and effective permissions`);
115
+ else if (stableJson(actual.get(name)) !== stableJson(expected.get(name)))
116
+ differences.push(`${transport}: ${name} descriptor differs from the pin`);
117
+ }
118
+ for (const name of actual.keys())
119
+ if (!expected.has(name))
120
+ differences.push(`${transport}: unexpected tool ${name}`);
121
+ }
122
+ if (stableJson(direct.metadata) !== stableJson(proxied.metadata))
123
+ differences.push("HTTP/stdio view metadata differs");
124
+ const descriptorHash = (tools) => sha256(stableJson([...tools].sort((a, b) => a.name.localeCompare(b.name))));
125
+ return {
126
+ schemaVersion: "honua.setup-catalog-parity/v1",
127
+ scope: "catalog-parity-only",
128
+ candidateId: manifest.candidateId,
129
+ serverImage: manifest.serverImage,
130
+ packages: manifest.packages.map(({ name, version, integrity }) => ({ name, version, integrity })),
131
+ manifestSha256,
132
+ view: manifest.view,
133
+ revision: manifest.revision,
134
+ revisionDigest: manifest.revisionDigest,
135
+ expectedTools: manifest.tools.length,
136
+ directTools: direct.tools.length,
137
+ proxiedTools: proxied.tools.length,
138
+ directDescriptorSha256: descriptorHash(direct.tools),
139
+ proxiedDescriptorSha256: descriptorHash(proxied.tools),
140
+ pass: differences.length === 0,
141
+ differences,
142
+ };
143
+ }
144
+ export async function runSetupParityCertification(manifestPath, manifestSha256, outputPath, env = process.env) {
145
+ const manifest = parseSetupCatalogManifest(await readFile(manifestPath, "utf8"), manifestSha256);
146
+ const options = resolveProxyOptions(env);
147
+ const upstream = await connectUpstream(options);
148
+ const proxied = new Client({ name: "honua-setup-parity", version: "1.0.0" });
149
+ const transport = new StdioClientTransport({
150
+ command: process.execPath,
151
+ args: [fileURLToPath(new URL("../proxy.js", import.meta.url))],
152
+ env: Object.fromEntries(Object.entries(env).filter((entry) => entry[1] !== undefined)),
153
+ stderr: "pipe",
154
+ });
155
+ // The proxy may report upstream error bodies. Never copy those into a receipt or terminal log.
156
+ transport.stderr?.on("data", () => { });
157
+ try {
158
+ await proxied.connect(transport);
159
+ const direct = await readSetupCatalog(upstream, manifest.view);
160
+ const forwarded = await readSetupCatalog(proxied, manifest.view);
161
+ const receipt = certifySetupCatalogParity(manifest, manifestSha256, direct, forwarded);
162
+ await writeFile(outputPath, `${JSON.stringify(receipt, null, 2)}\n`, "utf8");
163
+ return receipt;
164
+ }
165
+ finally {
166
+ await proxied.close().catch(() => { });
167
+ await transport.close().catch(() => { });
168
+ await upstream.close().catch(() => { });
169
+ }
170
+ }
171
+ if (isMainEntrypoint(import.meta.url)) {
172
+ const [manifestPath, digest, outputPath] = process.argv.slice(2);
173
+ if (!manifestPath || !digest || !outputPath || process.argv.length !== 5) {
174
+ process.stderr.write("Usage: setup-parity <server-manifest.json> <pinned-sha256> <receipt.json>\n");
175
+ process.exitCode = 1;
176
+ }
177
+ else {
178
+ runSetupParityCertification(manifestPath, digest, outputPath)
179
+ .then((receipt) => {
180
+ process.stdout.write(`${receipt.pass ? "PASS" : "FAIL"}: bounded setup catalog parity (${receipt.expectedTools} enabled tools)\n`);
181
+ process.exitCode = receipt.pass ? 0 : 1;
182
+ })
183
+ .catch(() => {
184
+ process.stderr.write("Setup parity could not complete; verify the pinned manifest, endpoint, credentials and server view. Upstream details suppressed.\n");
185
+ process.exitCode = 1;
186
+ });
187
+ }
188
+ }
@@ -22,7 +22,7 @@ import { runEval } from "./runner.js";
22
22
  * explicit request yields an honest driverError, not a silent
23
23
  * skip. Without --driver, drivers are auto-resolved from env.
24
24
  * --corpus <name> Select the corpus explicitly: analyst | operator | northstar |
25
- * standalone | ogc | all. This overrides HONUA_EVAL_CORPUS and is how the `eval:live`
25
+ * standalone | ogc | workflow | all. This overrides HONUA_EVAL_CORPUS and is how the `eval:live`
26
26
  * script targets the operator surface without relying on shell env
27
27
  * (cross-platform). `northstar` targets the three P1-gate
28
28
  * workflows; `standalone` and `ogc` target the two platform-free
@@ -39,7 +39,7 @@ import { runEval } from "./runner.js";
39
39
  * chain), and live remote MCP runs when HONUA_MCP_REMOTE_URL is set. Keys are
40
40
  * never hardcoded — they come from the environment.
41
41
  */
42
- const CORPUS_NAMES = ["analyst", "operator", "northstar", "standalone", "ogc", "all"];
42
+ const CORPUS_NAMES = ["analyst", "operator", "northstar", "standalone", "ogc", "workflow", "all"];
43
43
  function parseArgs(argv) {
44
44
  let artifactOnly = false;
45
45
  let offline = false;
@@ -1 +1 @@
1
- {"version":3,"file":"corpus.d.ts","sourceRoot":"","sources":["../../../src/eval/corpus.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,MAAM,EAAE,QAAQ,EAkG5B,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,QAAQ,EAAE,CAmB9E;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAEhF;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAEzE"}
1
+ {"version":3,"file":"corpus.d.ts","sourceRoot":"","sources":["../../../src/eval/corpus.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAG3C;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,MAAM,EAAE,QAAQ,EAkG5B,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,QAAQ,EAAE,CAmB9E;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAEhF;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAEzE"}
@@ -2,6 +2,7 @@ import { NORTHSTAR_CORPUS } from "./northstar-corpus.js";
2
2
  import { OGC_CORPUS } from "./ogc-corpus.js";
3
3
  import { OPERATOR_CORPUS } from "./operator-corpus.js";
4
4
  import { STANDALONE_CORPUS } from "./standalone-corpus.js";
5
+ import { WORKFLOW_CORPUS } from "./workflow-corpus.js";
5
6
  /**
6
7
  * Held-out corpus of GIS workflows for the cross-model eval (honua-server #1956).
7
8
  *
@@ -132,12 +133,12 @@ export function resolveCorpus(env = process.env) {
132
133
  return STANDALONE_CORPUS;
133
134
  case "ogc":
134
135
  return OGC_CORPUS;
136
+ case "workflow":
137
+ return WORKFLOW_CORPUS;
135
138
  case "all":
136
- // `all` spans the Honua-surface corpora only. The platform-free standalone
137
- // corpus targets a DIFFERENT surface (a plain FeatureServer fixture) and is
138
- // selected explicitly via `standalone`; mixing it here would run its
139
- // census-specific scenarios against an operator surface.
140
- return [...CORPUS, ...OPERATOR_CORPUS, ...NORTHSTAR_CORPUS];
139
+ // `all` spans the Honua-surface corpora. Standalone and OGC stay out:
140
+ // they target a different fixture surface. Workflow is the same surface.
141
+ return [...CORPUS, ...OPERATOR_CORPUS, ...NORTHSTAR_CORPUS, ...WORKFLOW_CORPUS];
141
142
  default:
142
143
  return CORPUS;
143
144
  }
@@ -1 +1 @@
1
- {"version":3,"file":"grade.d.ts","sourceRoot":"","sources":["../../../src/eval/grade.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAE9E,oGAAoG;AACpG,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAQjF;AAED;;;;;;;;GAQG;AACH,wBAAgB,KAAK,CAAC,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,kBAAkB,GAAG,aAAa,CAgEvF"}
1
+ {"version":3,"file":"grade.d.ts","sourceRoot":"","sources":["../../../src/eval/grade.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAE9E,oGAAoG;AACpG,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAQjF;AAED;;;;;;;;GAQG;AACH,wBAAgB,KAAK,CAAC,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,kBAAkB,GAAG,aAAa,CA4FvF"}
@@ -24,6 +24,9 @@ export function grade(scenario, transcript) {
24
24
  errorCount: transcript.errorCount,
25
25
  };
26
26
  const { criteria } = scenario;
27
+ if (transcript.blockedReason) {
28
+ return { ...base, outcome: "blocked", violations: [transcript.blockedReason] };
29
+ }
27
30
  if (transcript.driverError) {
28
31
  return { ...base, outcome: "error", violations: [transcript.driverError] };
29
32
  }
@@ -54,6 +57,28 @@ export function grade(scenario, transcript) {
54
57
  violations.push(`forbidden tool called: ${forbidden}`);
55
58
  }
56
59
  }
60
+ if (criteria.forbidApproval) {
61
+ for (const tool of calledTools) {
62
+ if (tool.toLowerCase().includes("approve")) {
63
+ violations.push(`approval tool called: ${tool}`);
64
+ }
65
+ }
66
+ }
67
+ if (criteria.failOnToolError && transcript.errorCount > 0) {
68
+ violations.push(`tool calls returned errors: ${transcript.errorCount}`);
69
+ }
70
+ const argumentText = transcript.steps.map((step) => JSON.stringify(step.args) ?? "").join("\n");
71
+ const compactArguments = argumentText.replace(/\s+/g, "");
72
+ for (const fragment of criteria.forbiddenArgumentText ?? []) {
73
+ if (compactArguments.includes(fragment.replace(/\s+/g, ""))) {
74
+ violations.push(`forbidden argument text: ${fragment}`);
75
+ }
76
+ }
77
+ for (const fragment of criteria.requiredArgumentText ?? []) {
78
+ if (!compactArguments.includes(fragment.replace(/\s+/g, ""))) {
79
+ violations.push(`argument text missing: ${fragment}`);
80
+ }
81
+ }
57
82
  const answer = transcript.finalAnswer.toLowerCase();
58
83
  for (const fragment of criteria.answerMustInclude ?? []) {
59
84
  if (!answer.includes(fragment.toLowerCase())) {
@@ -17,6 +17,13 @@ export interface EvalResult {
17
17
  * a target surface is still missing.
18
18
  */
19
19
  missingTools: string[];
20
+ /** Copied from the transcript so a saved artifact can be audited without the live run. */
21
+ skillId?: string;
22
+ journeyStage?: string;
23
+ journeyAction?: string;
24
+ surface?: WorkflowTranscript["surface"];
25
+ attribution?: WorkflowTranscript["attribution"];
26
+ capturedIds?: Record<string, string>;
20
27
  }
21
28
  /**
22
29
  * Auth mode the eval used to reach the MCP surface.
@@ -42,6 +49,8 @@ export interface ModelScorecard {
42
49
  fail: number;
43
50
  clarified: number;
44
51
  error: number;
52
+ /** Scenarios skipped because a required server profile is off. Not a pass. */
53
+ blocked: number;
45
54
  /** pass / scenarios. */
46
55
  successRate: number;
47
56
  /** clarified / scenarios. */
@@ -1 +1 @@
1
- {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../../src/eval/report.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../../src/eval/report.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAE3F,0EAA0E;AAC1E,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;IAClC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC;;;;;;OAMG;IACH,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,0FAA0F;IAC1F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,kBAAkB,CAAC,SAAS,CAAC,CAAC;IACxC,WAAW,CAAC,EAAE,kBAAkB,CAAC,aAAa,CAAC,CAAC;IAChD,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACtC;AAED;;;;;;;;;;GAUG;AACH,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,MAAM,CAAC;AAEnE,2EAA2E;AAC3E,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,kFAAkF;IAClF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAC;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,8EAA8E;IAC9E,OAAO,EAAE,MAAM,CAAC;IAChB,wBAAwB;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,6BAA6B;IAC7B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,yDAAyD;IACzD,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,UAAU;IACzB,aAAa,EAAE,CAAC,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,kFAAkF;IAClF,UAAU,EAAE,eAAe,CAAC;IAC5B,OAAO,EAAE;QACP,OAAO,EAAE,SAAS,GAAG,MAAM,CAAC;QAC5B,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,6EAA6E;QAC7E,IAAI,EAAE,QAAQ,CAAC;KAChB,CAAC;IACF,MAAM,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAC7C;;;;;OAKG;IACH,OAAO,EAAE;QACP,mBAAmB,EAAE,MAAM,CAAC;QAC5B,aAAa,EAAE,MAAM,EAAE,CAAC;QACxB,uBAAuB,EAAE,MAAM,EAAE,CAAC;KACnC,CAAC;IACF,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,OAAO,EAAE;QACP,8EAA8E;QAC9E,IAAI,EAAE,OAAO,CAAC;QACd,eAAe,EAAE,MAAM,CAAC;QACxB,mBAAmB,EAAE,MAAM,CAAC;QAC5B,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,SAAS,GAAG,MAAM,CAAC;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,IAAI,EAAE,QAAQ,CAAC;IACf,6EAA6E;IAC7E,UAAU,EAAE,eAAe,CAAC;IAC5B,8EAA8E;IAC9E,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,MAAM,EAAE,QAAQ,EAAE,CAAC;IACnB,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,MAAM,EAAE;QAAE,KAAK,EAAE,aAAa,CAAC;QAAC,UAAU,EAAE,kBAAkB,CAAA;KAAE,EAAE,CAAC;CACpE;AAaD,wBAAgB,cAAc,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,CAuF/D;AAED,mEAAmE;AACnE,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CA2DzD"}
@@ -22,6 +22,12 @@ export function assembleReport(input) {
22
22
  errorCount: grade.errorCount,
23
23
  driverError: transcript.driverError,
24
24
  missingTools: (requiredByScenario.get(grade.scenarioId) ?? []).filter((t) => !advertised.has(t)),
25
+ skillId: transcript.skillId,
26
+ journeyStage: transcript.journeyStage,
27
+ journeyAction: transcript.journeyAction,
28
+ surface: transcript.surface,
29
+ attribution: transcript.attribution,
30
+ capturedIds: transcript.capturedIds,
25
31
  }));
26
32
  const models = input.drivers.map((driver) => {
27
33
  const own = results.filter((r) => r.modelId === driver.id);
@@ -30,6 +36,7 @@ export function assembleReport(input) {
30
36
  const fail = own.filter((r) => r.outcome === "fail").length;
31
37
  const clarified = own.filter((r) => r.outcome === "clarified").length;
32
38
  const error = own.filter((r) => r.outcome === "error").length;
39
+ const blocked = own.filter((r) => r.outcome === "blocked").length;
33
40
  const withToolErrors = own.filter((r) => r.errorCount > 0).length;
34
41
  const totalToolErrors = own.reduce((acc, r) => acc + r.errorCount, 0);
35
42
  const rate = (n) => (scenarios === 0 ? 0 : Number((n / scenarios).toFixed(4)));
@@ -44,6 +51,7 @@ export function assembleReport(input) {
44
51
  fail,
45
52
  clarified,
46
53
  error,
54
+ blocked,
47
55
  successRate: rate(pass),
48
56
  clarificationRate: rate(clarified),
49
57
  editRate: rate(withToolErrors),
@@ -51,7 +59,9 @@ export function assembleReport(input) {
51
59
  };
52
60
  });
53
61
  const control = models.find((m) => m.vendor === "deterministic");
54
- const controlPass = control ? control.fail === 0 && control.error === 0 && control.scenarios > 0 : false;
62
+ const controlPass = control
63
+ ? control.fail === 0 && control.error === 0 && control.blocked === 0 && control.scenarios > 0
64
+ : false;
55
65
  const liveModelsEvaluated = models.filter((m) => m.vendor !== "deterministic" && m.scenarios > 0).length;
56
66
  const requiredTools = corpusRequiredTools(input.corpus);
57
67
  const unresolvedRequiredTools = requiredTools.filter((t) => !advertised.has(t));
@@ -103,13 +113,13 @@ export function renderMarkdown(report) {
103
113
  lines.push("");
104
114
  lines.push("## Per-model scorecard");
105
115
  lines.push("");
106
- lines.push("| Model | Vendor | Avail | Pass | Fail | Clarified | Error | Success | Clarify | Edit |");
107
- lines.push("| --- | --- | :---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |");
116
+ lines.push("| Model | Vendor | Avail | Pass | Fail | Clarified | Error | Blocked | Success | Clarify | Edit |");
117
+ lines.push("| --- | --- | :---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |");
108
118
  for (const m of report.models) {
109
- lines.push(`| \`${m.id}\` | ${m.vendor} | ${m.available ? "yes" : "no"} | ${m.pass} | ${m.fail} | ${m.clarified} | ${m.error} | ${pct(m.successRate)} | ${pct(m.clarificationRate)} | ${pct(m.editRate)} |`);
119
+ lines.push(`| \`${m.id}\` | ${m.vendor} | ${m.available ? "yes" : "no"} | ${m.pass} | ${m.fail} | ${m.clarified} | ${m.error} | ${m.blocked} | ${pct(m.successRate)} | ${pct(m.clarificationRate)} | ${pct(m.editRate)} |`);
110
120
  }
111
121
  lines.push("");
112
- const failures = report.results.filter((r) => r.outcome === "fail" || r.outcome === "error");
122
+ const failures = report.results.filter((r) => r.outcome === "fail" || r.outcome === "error" || r.outcome === "blocked");
113
123
  if (failures.length > 0) {
114
124
  lines.push("## Non-passing results");
115
125
  lines.push("");
@@ -1 +1 @@
1
- {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../src/eval/runner.ts"],"names":[],"mappings":"AAWA,OAAO,EAAiB,KAAK,UAAU,EAAkB,MAAM,aAAa,CAAC;AAC7E,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAmC,MAAM,YAAY,CAAC;AAEzF;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,cAAc;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;IACxB,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC;IACpB,+EAA+E;IAC/E,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AA4GD,wBAAsB,OAAO,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,UAAU,CAAC,CAkD/E"}
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../src/eval/runner.ts"],"names":[],"mappings":"AAWA,OAAO,EAAiB,KAAK,UAAU,EAAkB,MAAM,aAAa,CAAC;AAC7E,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAmC,MAAM,YAAY,CAAC;AAGzF;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,cAAc;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;IACxB,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC;IACpB,+EAA+E;IAC/E,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AA4GD,wBAAsB,OAAO,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,UAAU,CAAC,CAmE/E"}
@@ -10,6 +10,7 @@ import { isOgcCorpus, isStandaloneCorpus, resolveCorpus } from "./corpus.js";
10
10
  import { resolveDrivers } from "./drivers/index.js";
11
11
  import { grade } from "./grade.js";
12
12
  import { assembleReport } from "./report.js";
13
+ import { annotateTranscript, blockedTranscript, redactTranscript, unavailableProfiles } from "./workflow-corpus.js";
13
14
  /** Truthy-flag parse shared with the driver opt-ins (0/false/no/off ⇒ false). */
14
15
  function isTruthy(value) {
15
16
  if (typeof value !== "string") {
@@ -110,10 +111,24 @@ export async function runEval(options = {}) {
110
111
  inputSchema: t.inputSchema,
111
112
  }));
112
113
  const ctx = buildContext(surface.client, tools);
114
+ const profilesConfigured = env.HONUA_SERVER_PROFILES !== undefined;
115
+ const activeProfiles = (env.HONUA_SERVER_PROFILES ?? "")
116
+ .split(",")
117
+ .map((profile) => profile.trim())
118
+ .filter((profile) => profile.length > 0);
119
+ const advertised = new Set(tools.map((tool) => tool.name));
113
120
  const graded = [];
114
121
  for (const driver of drivers) {
115
122
  for (const scenario of corpus) {
116
- const transcript = await driver.runWorkflow(scenario, ctx);
123
+ const missingProfiles = profilesConfigured ? unavailableProfiles(scenario, activeProfiles) : [];
124
+ const toolsPresent = scenario.criteria.requiredTools.every((tool) => advertised.has(tool));
125
+ // An unset HONUA_SERVER_PROFILES is unknown, not proof the profiles are off.
126
+ // Advertised tools are stronger evidence than a partial profile list.
127
+ const missing = toolsPresent ? [] : missingProfiles;
128
+ const raw = missing.length > 0
129
+ ? blockedTranscript(driver.id, scenario, missing)
130
+ : await driver.runWorkflow(scenario, ctx);
131
+ const transcript = redactTranscript(annotateTranscript(raw, scenario, driver.vendor === "deterministic" ? "HARNESS_DRIVEN" : "MODEL_SELECTED"));
117
132
  graded.push({ grade: grade(scenario, transcript), transcript });
118
133
  }
119
134
  }