edgegate-mcp 0.1.2 → 0.2.1
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/README.md +3 -1
- package/dist/client.d.ts +3 -1
- package/dist/client.js +6 -0
- package/dist/client.js.map +1 -1
- package/dist/server.js +19 -0
- package/dist/server.js.map +1 -1
- package/dist/tools/compare_runs.d.ts +35 -0
- package/dist/tools/compare_runs.js +409 -0
- package/dist/tools/compare_runs.js.map +1 -0
- package/dist/tools/export_run_report.d.ts +35 -0
- package/dist/tools/export_run_report.js +327 -0
- package/dist/tools/export_run_report.js.map +1 -0
- package/dist/types.d.ts +43 -0
- package/dist/version.d.ts +2 -2
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/skills/edgegate-status.md +8 -0
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ MCP server for [EdgeGate](https://edgegate.frozo.ai) — set up edge-AI regressi
|
|
|
4
4
|
|
|
5
5
|
## What does it do?
|
|
6
6
|
|
|
7
|
-
EdgeGate runs AI model regression tests on real Snapdragon hardware via Qualcomm AI Hub, then produces signed evidence bundles you can attach to CI gates. This npm package exposes EdgeGate's REST API as
|
|
7
|
+
EdgeGate runs AI model regression tests on real Snapdragon hardware via Qualcomm AI Hub, then produces signed evidence bundles you can attach to CI gates. This npm package exposes EdgeGate's REST API as 9 MCP tools, plus 4 bundled skills, so you can drive the whole flow from a prompt:
|
|
8
8
|
|
|
9
9
|
```
|
|
10
10
|
> Use the edgegate MCP to set up a CI gate for my MobileNet ONNX model.
|
|
@@ -67,6 +67,8 @@ See [docs/tools.md](./docs/tools.md) for the full tool reference. Quick list:
|
|
|
67
67
|
| `edgegate_get_report` | List recent runs |
|
|
68
68
|
| `edgegate_get_audit_report` | Fetch the signed audit PDF |
|
|
69
69
|
| `edgegate_setup_github_action` | Generate the GitHub Actions workflow + secret commands |
|
|
70
|
+
| `edgegate_compare_runs` | Diff two runs — gate flips, metric deltas, per-device breakdown, REGRESSION / IMPROVEMENT / NEUTRAL verdict |
|
|
71
|
+
| `edgegate_export_run_report` | Save a complete run report as a markdown file to disk (returns the file path + preview) |
|
|
70
72
|
|
|
71
73
|
## Skills
|
|
72
74
|
|
package/dist/client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { APIKeyCreateResponse, Pipeline, RunBundle, RunDetail, RunSummary, Workspace, WorkflowTemplate } from "./types.js";
|
|
1
|
+
import type { APIKeyCreateResponse, Pipeline, RunBundle, RunComparison, RunDetail, RunSummary, Workspace, WorkflowTemplate } from "./types.js";
|
|
2
2
|
export interface EdgeGateClientOptions {
|
|
3
3
|
apiUrl: string;
|
|
4
4
|
apiKey: string;
|
|
@@ -60,6 +60,8 @@ export declare class EdgeGateClient {
|
|
|
60
60
|
}): Promise<RunSummary>;
|
|
61
61
|
getRun(workspaceId: string, runId: string): Promise<RunDetail>;
|
|
62
62
|
listRuns(workspaceId: string, limit?: number): Promise<RunSummary[]>;
|
|
63
|
+
listRunsByPipeline(workspaceId: string, pipelineId: string, limit?: number): Promise<RunSummary[]>;
|
|
64
|
+
getRunDiff(workspaceId: string, runId: string): Promise<RunComparison>;
|
|
63
65
|
getRunBundle(workspaceId: string, runId: string): Promise<RunBundle>;
|
|
64
66
|
getWorkflowTemplate(workspaceId: string): Promise<WorkflowTemplate>;
|
|
65
67
|
private request;
|
package/dist/client.js
CHANGED
|
@@ -48,6 +48,12 @@ export class EdgeGateClient {
|
|
|
48
48
|
async listRuns(workspaceId, limit = 20) {
|
|
49
49
|
return this.request("GET", `/v1/workspaces/${workspaceId}/runs?limit=${limit}`);
|
|
50
50
|
}
|
|
51
|
+
async listRunsByPipeline(workspaceId, pipelineId, limit = 20) {
|
|
52
|
+
return this.request("GET", `/v1/workspaces/${workspaceId}/runs?pipeline_id=${pipelineId}&limit=${limit}`);
|
|
53
|
+
}
|
|
54
|
+
async getRunDiff(workspaceId, runId) {
|
|
55
|
+
return this.request("GET", `/v1/workspaces/${workspaceId}/runs/${runId}/diff`);
|
|
56
|
+
}
|
|
51
57
|
async getRunBundle(workspaceId, runId) {
|
|
52
58
|
return this.request("GET", `/v1/workspaces/${workspaceId}/runs/${runId}/bundle`);
|
|
53
59
|
}
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/server.js
CHANGED
|
@@ -11,6 +11,8 @@ import { checkStatusHandler, checkStatusInputSchema } from "./tools/check_status
|
|
|
11
11
|
import { getReportHandler, getReportInputSchema } from "./tools/get_report.js";
|
|
12
12
|
import { getAuditReportHandler, getAuditReportInputSchema } from "./tools/get_audit_report.js";
|
|
13
13
|
import { setupGithubActionHandler, setupGithubActionInputSchema, } from "./tools/setup_github_action.js";
|
|
14
|
+
import { compareRunsHandler, compareRunsInputSchema, } from "./tools/compare_runs.js";
|
|
15
|
+
import { exportRunReportHandler, exportRunReportInputSchema, } from "./tools/export_run_report.js";
|
|
14
16
|
const TOOLS = [
|
|
15
17
|
{
|
|
16
18
|
name: "edgegate_setup_workspace",
|
|
@@ -60,6 +62,23 @@ const TOOLS = [
|
|
|
60
62
|
schema: setupGithubActionInputSchema,
|
|
61
63
|
handler: setupGithubActionHandler,
|
|
62
64
|
},
|
|
65
|
+
{
|
|
66
|
+
name: "edgegate_compare_runs",
|
|
67
|
+
description: "Diff two EdgeGate runs in the same pipeline — metrics delta, gate flips (✓→✗ regressions " +
|
|
68
|
+
"and ✗→✓ recoveries), per-device breakdown, and an overall verdict (REGRESSION / IMPROVEMENT / " +
|
|
69
|
+
"NEUTRAL / NO BASELINE). When baseline_run_id is omitted, auto-selects the most recent " +
|
|
70
|
+
"PASSED run from the same pipeline as the baseline.",
|
|
71
|
+
schema: compareRunsInputSchema,
|
|
72
|
+
handler: compareRunsHandler,
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: "edgegate_export_run_report",
|
|
76
|
+
description: "Download a human-readable markdown report for an EdgeGate run and save it to disk. " +
|
|
77
|
+
"Returns the absolute file path plus a preview of the first 30 lines. " +
|
|
78
|
+
"Optionally includes a run-vs-baseline diff section (include_diff=true).",
|
|
79
|
+
schema: exportRunReportInputSchema,
|
|
80
|
+
handler: exportRunReportHandler,
|
|
81
|
+
},
|
|
63
82
|
];
|
|
64
83
|
function getClient() {
|
|
65
84
|
const apiUrl = process.env.EDGEGATE_API_URL ?? "https://edgegateapi.frozo.ai";
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EACL,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,OAAO,EAAE,qBAAqB,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAC9F,OAAO,EAAE,qBAAqB,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAC9F,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzE,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACrF,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC/E,OAAO,EAAE,qBAAqB,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AAC/F,OAAO,EACL,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* edgegate_compare_runs — run-over-run diff with smart auto-baseline selection.
|
|
3
|
+
*
|
|
4
|
+
* When baseline_run_id is omitted:
|
|
5
|
+
* 1. Fetch candidate run's pipeline_id
|
|
6
|
+
* 2. List last 20 runs in that pipeline
|
|
7
|
+
* 3. Pick the most recent PASSED run with a bundle (excluding candidate itself)
|
|
8
|
+
* 4. Fallback: most recent completed run (any status) excluding candidate
|
|
9
|
+
* 5. If nothing found: "NO BASELINE" response
|
|
10
|
+
*
|
|
11
|
+
* The backend already exposes GET /v1/workspaces/{id}/runs/{run_id}/diff which
|
|
12
|
+
* returns the pre-computed, signed diff embedded in the evidence bundle (commit
|
|
13
|
+
* 41167a6). We call that endpoint for the candidate run — it internally uses
|
|
14
|
+
* the baseline the Celery task stored at completion time. When the user
|
|
15
|
+
* explicitly provides a baseline_run_id that differs from what the backend
|
|
16
|
+
* stored, we fall back to client-side diff from both runs' detail endpoints.
|
|
17
|
+
*/
|
|
18
|
+
import { z } from "zod";
|
|
19
|
+
import { EdgeGateClient } from "../client.js";
|
|
20
|
+
import type { ToolResult } from "./setup_workspace.js";
|
|
21
|
+
export declare const compareRunsInputSchema: z.ZodObject<{
|
|
22
|
+
workspace_id: z.ZodString;
|
|
23
|
+
run_id: z.ZodString;
|
|
24
|
+
baseline_run_id: z.ZodOptional<z.ZodString>;
|
|
25
|
+
}, "strip", z.ZodTypeAny, {
|
|
26
|
+
workspace_id: string;
|
|
27
|
+
run_id: string;
|
|
28
|
+
baseline_run_id?: string | undefined;
|
|
29
|
+
}, {
|
|
30
|
+
workspace_id: string;
|
|
31
|
+
run_id: string;
|
|
32
|
+
baseline_run_id?: string | undefined;
|
|
33
|
+
}>;
|
|
34
|
+
export type CompareRunsInput = z.infer<typeof compareRunsInputSchema>;
|
|
35
|
+
export declare function compareRunsHandler(client: EdgeGateClient, input: CompareRunsInput): Promise<ToolResult>;
|
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* edgegate_compare_runs — run-over-run diff with smart auto-baseline selection.
|
|
3
|
+
*
|
|
4
|
+
* When baseline_run_id is omitted:
|
|
5
|
+
* 1. Fetch candidate run's pipeline_id
|
|
6
|
+
* 2. List last 20 runs in that pipeline
|
|
7
|
+
* 3. Pick the most recent PASSED run with a bundle (excluding candidate itself)
|
|
8
|
+
* 4. Fallback: most recent completed run (any status) excluding candidate
|
|
9
|
+
* 5. If nothing found: "NO BASELINE" response
|
|
10
|
+
*
|
|
11
|
+
* The backend already exposes GET /v1/workspaces/{id}/runs/{run_id}/diff which
|
|
12
|
+
* returns the pre-computed, signed diff embedded in the evidence bundle (commit
|
|
13
|
+
* 41167a6). We call that endpoint for the candidate run — it internally uses
|
|
14
|
+
* the baseline the Celery task stored at completion time. When the user
|
|
15
|
+
* explicitly provides a baseline_run_id that differs from what the backend
|
|
16
|
+
* stored, we fall back to client-side diff from both runs' detail endpoints.
|
|
17
|
+
*/
|
|
18
|
+
import { z } from "zod";
|
|
19
|
+
import { EdgeGateError } from "../client.js";
|
|
20
|
+
export const compareRunsInputSchema = z.object({
|
|
21
|
+
workspace_id: z.string().uuid(),
|
|
22
|
+
run_id: z.string().uuid().describe("Candidate run to evaluate"),
|
|
23
|
+
baseline_run_id: z
|
|
24
|
+
.string()
|
|
25
|
+
.uuid()
|
|
26
|
+
.optional()
|
|
27
|
+
.describe("Baseline to compare against. When omitted, auto-selects the most recent " +
|
|
28
|
+
"PASSED run from the same pipeline (excluding the candidate itself), " +
|
|
29
|
+
"or the most recent completed run as a fallback."),
|
|
30
|
+
});
|
|
31
|
+
// Metrics where lower is better (high delta% is bad)
|
|
32
|
+
const LOWER_IS_BETTER = new Set(["inference_time_ms", "peak_memory_mb", "latency_ms"]);
|
|
33
|
+
// Threshold for "significant regression" in lower-is-better metrics
|
|
34
|
+
const REGRESSION_THRESHOLD_PCT = 25;
|
|
35
|
+
export async function compareRunsHandler(client, input) {
|
|
36
|
+
try {
|
|
37
|
+
const { workspace_id, run_id, baseline_run_id } = input;
|
|
38
|
+
// --- Fetch candidate run detail (need pipeline_id for auto-baseline) ---
|
|
39
|
+
let candidateRun;
|
|
40
|
+
try {
|
|
41
|
+
candidateRun = await client.getRun(workspace_id, run_id);
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
if (err instanceof EdgeGateError) {
|
|
45
|
+
return {
|
|
46
|
+
isError: true,
|
|
47
|
+
content: [
|
|
48
|
+
{
|
|
49
|
+
type: "text",
|
|
50
|
+
text: `Could not fetch candidate run ${run_id}: ${err.detail}`,
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
throw err;
|
|
56
|
+
}
|
|
57
|
+
// --- Try the backend /diff endpoint first (Scenario A fast path) ---
|
|
58
|
+
// The backend stores the diff from the previous pipeline run at completion.
|
|
59
|
+
// If the caller did NOT supply a baseline_run_id (or it matches what the
|
|
60
|
+
// backend would pick), use the pre-computed signed diff directly.
|
|
61
|
+
if (!baseline_run_id) {
|
|
62
|
+
try {
|
|
63
|
+
const comparison = await client.getRunDiff(workspace_id, run_id);
|
|
64
|
+
// Backend returned a diff — render it
|
|
65
|
+
return { content: [{ type: "text", text: renderComparison(comparison, candidateRun) }] };
|
|
66
|
+
}
|
|
67
|
+
catch (diffErr) {
|
|
68
|
+
if (diffErr instanceof EdgeGateError && diffErr.status === 404) {
|
|
69
|
+
// No server-side diff yet: either first run or still in flight.
|
|
70
|
+
// Attempt client-side auto-baseline selection.
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
// Unexpected error — propagate
|
|
74
|
+
if (diffErr instanceof EdgeGateError) {
|
|
75
|
+
return {
|
|
76
|
+
isError: true,
|
|
77
|
+
content: [
|
|
78
|
+
{
|
|
79
|
+
type: "text",
|
|
80
|
+
text: `EdgeGate returned ${diffErr.status} fetching diff: ${diffErr.detail}`,
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
throw diffErr;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// --- Auto-baseline or explicit baseline: client-side path ---
|
|
90
|
+
let resolvedBaselineId = baseline_run_id ?? null;
|
|
91
|
+
if (!resolvedBaselineId) {
|
|
92
|
+
// Auto-select: list recent runs in the same pipeline
|
|
93
|
+
resolvedBaselineId = await pickAutoBaseline(client, workspace_id, candidateRun);
|
|
94
|
+
if (!resolvedBaselineId) {
|
|
95
|
+
return {
|
|
96
|
+
content: [
|
|
97
|
+
{
|
|
98
|
+
type: "text",
|
|
99
|
+
text: formatNoBaseline(candidateRun),
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Fetch baseline run detail
|
|
106
|
+
let baselineRun;
|
|
107
|
+
try {
|
|
108
|
+
baselineRun = await client.getRun(workspace_id, resolvedBaselineId);
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
if (err instanceof EdgeGateError) {
|
|
112
|
+
return {
|
|
113
|
+
isError: true,
|
|
114
|
+
content: [
|
|
115
|
+
{
|
|
116
|
+
type: "text",
|
|
117
|
+
text: `Could not fetch baseline run ${resolvedBaselineId}: ${err.detail}`,
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
throw err;
|
|
123
|
+
}
|
|
124
|
+
// Build client-side diff
|
|
125
|
+
const comparison = buildClientSideDiff(candidateRun, baselineRun);
|
|
126
|
+
return { content: [{ type: "text", text: renderComparison(comparison, candidateRun) }] };
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
if (err instanceof EdgeGateError) {
|
|
130
|
+
return {
|
|
131
|
+
isError: true,
|
|
132
|
+
content: [{ type: "text", text: `EdgeGate returned ${err.status}: ${err.detail}` }],
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
throw err;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
// ─── Auto-baseline selection ───────────────────────────────────────────────
|
|
139
|
+
async function pickAutoBaseline(client, workspaceId, candidateRun) {
|
|
140
|
+
const pipelineId = candidateRun.pipeline_id;
|
|
141
|
+
let runs;
|
|
142
|
+
try {
|
|
143
|
+
runs = await client.listRunsByPipeline(workspaceId, pipelineId, 20);
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
// Fall back to listing all runs if pipeline filter fails
|
|
147
|
+
try {
|
|
148
|
+
runs = await client.listRuns(workspaceId, 20);
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// Filter to same pipeline, excluding candidate itself
|
|
155
|
+
const eligible = runs.filter((r) => r.id !== candidateRun.id && r.pipeline_id === pipelineId);
|
|
156
|
+
// Priority 1: most recent PASSED with a bundle
|
|
157
|
+
// (RunSummary doesn't have bundle_artifact_id, but passed status implies a bundle)
|
|
158
|
+
const passedRun = eligible.find((r) => r.status === "passed");
|
|
159
|
+
if (passedRun)
|
|
160
|
+
return passedRun.id;
|
|
161
|
+
// Priority 2: most recent completed run (any terminal status)
|
|
162
|
+
const completedRun = eligible.find((r) => ["passed", "failed", "error"].includes(r.status));
|
|
163
|
+
if (completedRun)
|
|
164
|
+
return completedRun.id;
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
// ─── Client-side diff construction ────────────────────────────────────────
|
|
168
|
+
function buildClientSideDiff(candidate, baseline) {
|
|
169
|
+
const candidateMetrics = candidate.normalized_metrics ?? {};
|
|
170
|
+
const baselineMetrics = baseline.normalized_metrics ?? {};
|
|
171
|
+
// Metric deltas
|
|
172
|
+
const allMetricKeys = new Set([
|
|
173
|
+
...Object.keys(candidateMetrics),
|
|
174
|
+
...Object.keys(baselineMetrics),
|
|
175
|
+
]);
|
|
176
|
+
const metric_deltas = {};
|
|
177
|
+
for (const k of allMetricKeys) {
|
|
178
|
+
const cur = candidateMetrics[k] ?? null;
|
|
179
|
+
const prev = baselineMetrics[k] ?? null;
|
|
180
|
+
const delta = cur !== null && prev !== null ? cur - prev : null;
|
|
181
|
+
const delta_pct = delta !== null && prev !== null && prev !== 0 ? (delta / prev) * 100 : null;
|
|
182
|
+
metric_deltas[k] = { current: cur, previous: prev, delta, delta_pct };
|
|
183
|
+
}
|
|
184
|
+
// Gate flips
|
|
185
|
+
const candidateGates = candidate.gates_eval?.gates ?? [];
|
|
186
|
+
const baselineGates = baseline.gates_eval?.gates ?? [];
|
|
187
|
+
const prevByMetric = new Map(baselineGates.map((g) => [g.metric, g]));
|
|
188
|
+
const currByMetric = new Map(candidateGates.map((g) => [g.metric, g]));
|
|
189
|
+
const allGateMetrics = new Set([...prevByMetric.keys(), ...currByMetric.keys()]);
|
|
190
|
+
const gate_flips = [];
|
|
191
|
+
for (const m of [...allGateMetrics].sort()) {
|
|
192
|
+
const prev = prevByMetric.get(m) ?? null;
|
|
193
|
+
const curr = currByMetric.get(m) ?? null;
|
|
194
|
+
let transition;
|
|
195
|
+
if (!prev)
|
|
196
|
+
transition = "new";
|
|
197
|
+
else if (!curr)
|
|
198
|
+
transition = "removed";
|
|
199
|
+
else {
|
|
200
|
+
const p = prev.passed;
|
|
201
|
+
const c = curr.passed;
|
|
202
|
+
if (c && p)
|
|
203
|
+
transition = "unchanged";
|
|
204
|
+
else if (c && !p)
|
|
205
|
+
transition = "improved";
|
|
206
|
+
else if (!c && p)
|
|
207
|
+
transition = "regressed";
|
|
208
|
+
else
|
|
209
|
+
transition = "still_failing";
|
|
210
|
+
}
|
|
211
|
+
gate_flips.push({
|
|
212
|
+
metric: m,
|
|
213
|
+
transition,
|
|
214
|
+
previous: prev
|
|
215
|
+
? {
|
|
216
|
+
passed: prev.passed,
|
|
217
|
+
threshold: prev.threshold,
|
|
218
|
+
operator: prev.operator,
|
|
219
|
+
actual_value: prev.actual_value,
|
|
220
|
+
}
|
|
221
|
+
: null,
|
|
222
|
+
current: curr
|
|
223
|
+
? {
|
|
224
|
+
passed: curr.passed,
|
|
225
|
+
threshold: curr.threshold,
|
|
226
|
+
operator: curr.operator,
|
|
227
|
+
actual_value: curr.actual_value,
|
|
228
|
+
}
|
|
229
|
+
: null,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
return {
|
|
233
|
+
current_run_id: candidate.id,
|
|
234
|
+
previous_run_id: baseline.id,
|
|
235
|
+
diff_sha256: null, // client-side, not signed
|
|
236
|
+
diff: {
|
|
237
|
+
current_run_id: candidate.id,
|
|
238
|
+
previous_run_id: baseline.id,
|
|
239
|
+
current_commit: {},
|
|
240
|
+
previous_commit: {},
|
|
241
|
+
current_completed_at: candidate.completed_at,
|
|
242
|
+
previous_completed_at: baseline.completed_at,
|
|
243
|
+
metric_deltas,
|
|
244
|
+
gate_flips,
|
|
245
|
+
per_device: null,
|
|
246
|
+
per_cell: null,
|
|
247
|
+
is_baseline: false,
|
|
248
|
+
},
|
|
249
|
+
created_at: new Date().toISOString(),
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
// ─── Rendering ─────────────────────────────────────────────────────────────
|
|
253
|
+
function renderComparison(comparison, candidateRun) {
|
|
254
|
+
const diff = comparison.diff;
|
|
255
|
+
const lines = [];
|
|
256
|
+
// Header
|
|
257
|
+
lines.push(`## Run Comparison`, ``, `**Pipeline:** ${candidateRun.pipeline_name} (${candidateRun.pipeline_id})`, `**Candidate:** \`${comparison.current_run_id}\` ` +
|
|
258
|
+
`(${diff.current_completed_at ?? "in flight"})`, `**Baseline:** \`${comparison.previous_run_id ?? "—"}\` ` +
|
|
259
|
+
`(${diff.previous_completed_at ?? "—"})`, ``);
|
|
260
|
+
if (diff.is_baseline) {
|
|
261
|
+
lines.push(`> **NO BASELINE** — this is the first completed run in this pipeline.`);
|
|
262
|
+
lines.push(``);
|
|
263
|
+
return lines.join("\n");
|
|
264
|
+
}
|
|
265
|
+
// Commit context (only if server-side diff has it)
|
|
266
|
+
const cc = diff.current_commit;
|
|
267
|
+
const pc = diff.previous_commit;
|
|
268
|
+
if (cc?.sha || pc?.sha) {
|
|
269
|
+
lines.push(`### Commit Context`);
|
|
270
|
+
if (cc?.sha)
|
|
271
|
+
lines.push(`**Candidate:** \`${cc.sha}\` — ${cc.message ?? ""}`);
|
|
272
|
+
if (pc?.sha)
|
|
273
|
+
lines.push(`**Baseline:** \`${pc.sha}\` — ${pc.message ?? ""}`);
|
|
274
|
+
lines.push(``);
|
|
275
|
+
}
|
|
276
|
+
// Metrics
|
|
277
|
+
const metricKeys = Object.keys(diff.metric_deltas).sort();
|
|
278
|
+
if (metricKeys.length > 0) {
|
|
279
|
+
lines.push(`### Metrics`);
|
|
280
|
+
lines.push(`| Metric | Baseline | Candidate | Delta | Direction |`);
|
|
281
|
+
lines.push(`|---|---|---|---|---|`);
|
|
282
|
+
for (const k of metricKeys) {
|
|
283
|
+
const m = diff.metric_deltas[k];
|
|
284
|
+
const pct = m.delta_pct !== null ? `${m.delta_pct > 0 ? "+" : ""}${m.delta_pct.toFixed(1)}%` : "—";
|
|
285
|
+
const arrow = m.delta === null ? "" : m.delta > 0 ? "↑" : m.delta < 0 ? "↓" : "→";
|
|
286
|
+
const direction = m.delta === null ? "—" : buildDirectionLabel(k, m.delta);
|
|
287
|
+
lines.push(`| ${k} | ${fmt(m.previous)} | ${fmt(m.current)} | ${fmtDelta(m.delta)} (${pct}) ${arrow} | ${direction} |`);
|
|
288
|
+
}
|
|
289
|
+
lines.push(``);
|
|
290
|
+
}
|
|
291
|
+
// Gate flips
|
|
292
|
+
if (diff.gate_flips.length > 0) {
|
|
293
|
+
lines.push(`### Gate Status`);
|
|
294
|
+
lines.push(`| Gate | Baseline | Candidate | Status |`);
|
|
295
|
+
lines.push(`|---|---|---|---|`);
|
|
296
|
+
for (const gf of diff.gate_flips) {
|
|
297
|
+
const baseIcon = gateIcon(gf.previous?.passed ?? null);
|
|
298
|
+
const candIcon = gateIcon(gf.current?.passed ?? null);
|
|
299
|
+
const statusLabel = flipLabel(gf.transition);
|
|
300
|
+
lines.push(`| ${gf.metric} | ${baseIcon} | ${candIcon} | ${statusLabel} |`);
|
|
301
|
+
}
|
|
302
|
+
lines.push(``);
|
|
303
|
+
}
|
|
304
|
+
// Per-device breakdown
|
|
305
|
+
if (diff.per_device && Object.keys(diff.per_device).length > 0) {
|
|
306
|
+
lines.push(`### Per-Device Breakdown`);
|
|
307
|
+
for (const [device, metrics] of Object.entries(diff.per_device)) {
|
|
308
|
+
lines.push(`**${device}**`);
|
|
309
|
+
for (const [k, m] of Object.entries(metrics)) {
|
|
310
|
+
const pct = m.delta_pct !== null ? `${m.delta_pct > 0 ? "+" : ""}${m.delta_pct.toFixed(1)}%` : "—";
|
|
311
|
+
lines.push(` - ${k}: ${fmt(m.previous)} → ${fmt(m.current)} (${pct})`);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
lines.push(``);
|
|
315
|
+
}
|
|
316
|
+
// Verdict
|
|
317
|
+
const verdict = computeVerdict(diff.gate_flips, diff.metric_deltas);
|
|
318
|
+
lines.push(`### Verdict`);
|
|
319
|
+
lines.push(``);
|
|
320
|
+
lines.push(verdictBadge(verdict));
|
|
321
|
+
lines.push(``);
|
|
322
|
+
// Audit trail
|
|
323
|
+
lines.push(`### Audit Trail`);
|
|
324
|
+
if (comparison.diff_sha256) {
|
|
325
|
+
lines.push(`Diff SHA-256: \`${comparison.diff_sha256}\` (signed, embedded in evidence bundle)`);
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
lines.push(`Diff computed client-side (no SHA-256 — backend diff not yet available for this run)`);
|
|
329
|
+
}
|
|
330
|
+
if (candidateRun.bundle_artifact_id) {
|
|
331
|
+
lines.push(`Candidate bundle artifact: \`${candidateRun.bundle_artifact_id}\``);
|
|
332
|
+
}
|
|
333
|
+
if (comparison.previous_run_id) {
|
|
334
|
+
lines.push(`Baseline run ID: \`${comparison.previous_run_id}\``);
|
|
335
|
+
}
|
|
336
|
+
return lines.join("\n");
|
|
337
|
+
}
|
|
338
|
+
function formatNoBaseline(candidateRun) {
|
|
339
|
+
return [
|
|
340
|
+
`## Run Comparison`,
|
|
341
|
+
``,
|
|
342
|
+
`**Pipeline:** ${candidateRun.pipeline_name} (${candidateRun.pipeline_id})`,
|
|
343
|
+
`**Candidate:** \`${candidateRun.id}\``,
|
|
344
|
+
``,
|
|
345
|
+
`> **NO BASELINE** — this is the first completed run in pipeline "${candidateRun.pipeline_name}", ` +
|
|
346
|
+
`or no prior completed runs were found. There is nothing to compare against yet.`,
|
|
347
|
+
``,
|
|
348
|
+
`**Verdict: NO BASELINE**`,
|
|
349
|
+
].join("\n");
|
|
350
|
+
}
|
|
351
|
+
// ─── Helpers ───────────────────────────────────────────────────────────────
|
|
352
|
+
function fmt(v) {
|
|
353
|
+
return v === null ? "—" : v.toFixed(2);
|
|
354
|
+
}
|
|
355
|
+
function fmtDelta(v) {
|
|
356
|
+
if (v === null)
|
|
357
|
+
return "—";
|
|
358
|
+
return `${v > 0 ? "+" : ""}${v.toFixed(2)}`;
|
|
359
|
+
}
|
|
360
|
+
function gateIcon(passed) {
|
|
361
|
+
if (passed === null)
|
|
362
|
+
return "—";
|
|
363
|
+
return passed ? "✓" : "✗";
|
|
364
|
+
}
|
|
365
|
+
function flipLabel(transition) {
|
|
366
|
+
switch (transition) {
|
|
367
|
+
case "regressed": return "**REGRESSION** ✓→✗";
|
|
368
|
+
case "improved": return "RECOVERY ✗→✓";
|
|
369
|
+
case "unchanged": return "passing";
|
|
370
|
+
case "still_failing": return "still failing";
|
|
371
|
+
case "new": return "new gate";
|
|
372
|
+
case "removed": return "removed";
|
|
373
|
+
default: return transition;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
function buildDirectionLabel(metric, delta) {
|
|
377
|
+
if (delta === 0)
|
|
378
|
+
return "no change";
|
|
379
|
+
const lowerBetter = LOWER_IS_BETTER.has(metric);
|
|
380
|
+
if (lowerBetter) {
|
|
381
|
+
return delta > 0 ? "worse ↑" : "better ↓";
|
|
382
|
+
}
|
|
383
|
+
return delta > 0 ? "better ↑" : "worse ↓";
|
|
384
|
+
}
|
|
385
|
+
function computeVerdict(gateFlips, metricDeltas) {
|
|
386
|
+
const hasRegression = gateFlips.some((gf) => gf.transition === "regressed");
|
|
387
|
+
const hasRecovery = gateFlips.some((gf) => gf.transition === "improved");
|
|
388
|
+
// Also flag metric-only regression even if no gate flip
|
|
389
|
+
const significantMetricRegression = Object.entries(metricDeltas).some(([k, m]) => {
|
|
390
|
+
if (!LOWER_IS_BETTER.has(k))
|
|
391
|
+
return false;
|
|
392
|
+
return m.delta_pct !== null && m.delta_pct >= REGRESSION_THRESHOLD_PCT;
|
|
393
|
+
});
|
|
394
|
+
if (hasRegression || significantMetricRegression)
|
|
395
|
+
return "REGRESSION";
|
|
396
|
+
if (hasRecovery && !hasRegression)
|
|
397
|
+
return "IMPROVEMENT";
|
|
398
|
+
return "NEUTRAL";
|
|
399
|
+
}
|
|
400
|
+
function verdictBadge(verdict) {
|
|
401
|
+
switch (verdict) {
|
|
402
|
+
case "REGRESSION": return `**REGRESSION** — one or more gates regressed or a lower-is-better metric increased by ≥${REGRESSION_THRESHOLD_PCT}%.`;
|
|
403
|
+
case "IMPROVEMENT": return `**IMPROVEMENT** — previously-failing gates now pass; no regressions.`;
|
|
404
|
+
case "NEUTRAL": return `**NEUTRAL** — no gate flips and no significant metric regressions.`;
|
|
405
|
+
case "NO BASELINE": return `**NO BASELINE** — no prior run to compare against.`;
|
|
406
|
+
default: return `**${verdict}**`;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
//# sourceMappingURL=compare_runs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compare_runs.js","sourceRoot":"","sources":["../../src/tools/compare_runs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* edgegate_export_run_report — render a human-readable markdown report for a
|
|
3
|
+
* completed (or in-flight) EdgeGate run and write it to disk.
|
|
4
|
+
*
|
|
5
|
+
* Steps:
|
|
6
|
+
* 1. Fetch run detail via getRun
|
|
7
|
+
* 2. If the run is completed, fetch the evidence bundle via getRunBundle
|
|
8
|
+
* 3. Optionally fetch the run diff via getRunDiff (when include_diff=true)
|
|
9
|
+
* 4. Render a comprehensive markdown report
|
|
10
|
+
* 5. Write the file to disk (creating parent directories as needed)
|
|
11
|
+
* 6. Return the absolute file path + first ~30 lines of the report
|
|
12
|
+
*/
|
|
13
|
+
import { z } from "zod";
|
|
14
|
+
import { tmpdir } from "node:os";
|
|
15
|
+
import { EdgeGateClient } from "../client.js";
|
|
16
|
+
import type { ToolResult } from "./setup_workspace.js";
|
|
17
|
+
export declare const exportRunReportInputSchema: z.ZodObject<{
|
|
18
|
+
workspace_id: z.ZodString;
|
|
19
|
+
run_id: z.ZodString;
|
|
20
|
+
output_path: z.ZodOptional<z.ZodString>;
|
|
21
|
+
include_diff: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
22
|
+
}, "strip", z.ZodTypeAny, {
|
|
23
|
+
workspace_id: string;
|
|
24
|
+
run_id: string;
|
|
25
|
+
include_diff: boolean;
|
|
26
|
+
output_path?: string | undefined;
|
|
27
|
+
}, {
|
|
28
|
+
workspace_id: string;
|
|
29
|
+
run_id: string;
|
|
30
|
+
output_path?: string | undefined;
|
|
31
|
+
include_diff?: boolean | undefined;
|
|
32
|
+
}>;
|
|
33
|
+
export type ExportRunReportInput = z.infer<typeof exportRunReportInputSchema>;
|
|
34
|
+
export declare function exportRunReportHandler(client: EdgeGateClient, input: ExportRunReportInput): Promise<ToolResult>;
|
|
35
|
+
export { tmpdir as _tmpdir };
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* edgegate_export_run_report — render a human-readable markdown report for a
|
|
3
|
+
* completed (or in-flight) EdgeGate run and write it to disk.
|
|
4
|
+
*
|
|
5
|
+
* Steps:
|
|
6
|
+
* 1. Fetch run detail via getRun
|
|
7
|
+
* 2. If the run is completed, fetch the evidence bundle via getRunBundle
|
|
8
|
+
* 3. Optionally fetch the run diff via getRunDiff (when include_diff=true)
|
|
9
|
+
* 4. Render a comprehensive markdown report
|
|
10
|
+
* 5. Write the file to disk (creating parent directories as needed)
|
|
11
|
+
* 6. Return the absolute file path + first ~30 lines of the report
|
|
12
|
+
*/
|
|
13
|
+
import { z } from "zod";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { homedir, tmpdir } from "node:os";
|
|
16
|
+
import { mkdir, stat, writeFile } from "node:fs/promises";
|
|
17
|
+
import { EdgeGateError } from "../client.js";
|
|
18
|
+
import { VERSION } from "../version.js";
|
|
19
|
+
export const exportRunReportInputSchema = z.object({
|
|
20
|
+
workspace_id: z.string().uuid(),
|
|
21
|
+
run_id: z.string().uuid(),
|
|
22
|
+
output_path: z
|
|
23
|
+
.string()
|
|
24
|
+
.optional()
|
|
25
|
+
.describe("Where to write the markdown file. Defaults to `./edgegate-run-{id-short}.md` in the " +
|
|
26
|
+
"current working directory. If a directory, the file is named `edgegate-run-{id-short}.md` " +
|
|
27
|
+
"inside it. Supports `~` and relative paths."),
|
|
28
|
+
include_diff: z
|
|
29
|
+
.boolean()
|
|
30
|
+
.optional()
|
|
31
|
+
.default(false)
|
|
32
|
+
.describe("When true, also fetches the run-vs-baseline diff and appends a diff section to the report."),
|
|
33
|
+
});
|
|
34
|
+
// Terminal statuses that should have a bundle available
|
|
35
|
+
const TERMINAL_STATUSES = new Set(["passed", "failed", "error"]);
|
|
36
|
+
export async function exportRunReportHandler(client, input) {
|
|
37
|
+
try {
|
|
38
|
+
const { workspace_id, run_id, output_path, include_diff } = input;
|
|
39
|
+
// --- 1. Fetch run detail ---
|
|
40
|
+
let run;
|
|
41
|
+
try {
|
|
42
|
+
run = await client.getRun(workspace_id, run_id);
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
if (err instanceof EdgeGateError) {
|
|
46
|
+
return {
|
|
47
|
+
isError: true,
|
|
48
|
+
content: [
|
|
49
|
+
{
|
|
50
|
+
type: "text",
|
|
51
|
+
text: `Could not fetch run ${run_id}: ${err.detail}`,
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
throw err;
|
|
57
|
+
}
|
|
58
|
+
// --- 2. Fetch evidence bundle (only for terminal runs) ---
|
|
59
|
+
let bundle = null;
|
|
60
|
+
if (TERMINAL_STATUSES.has(run.status)) {
|
|
61
|
+
try {
|
|
62
|
+
bundle = await client.getRunBundle(workspace_id, run_id);
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
if (err instanceof EdgeGateError && (err.status === 404 || err.status === 409)) {
|
|
66
|
+
// Bundle not ready yet — proceed without it
|
|
67
|
+
bundle = null;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
throw err;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// --- 3. Optionally fetch diff ---
|
|
75
|
+
let comparison = null;
|
|
76
|
+
if (include_diff) {
|
|
77
|
+
try {
|
|
78
|
+
comparison = await client.getRunDiff(workspace_id, run_id);
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
if (err instanceof EdgeGateError && err.status === 404) {
|
|
82
|
+
// No diff yet — silently omit the section
|
|
83
|
+
comparison = null;
|
|
84
|
+
}
|
|
85
|
+
else if (err instanceof EdgeGateError) {
|
|
86
|
+
// Non-fatal — skip the diff section but note it
|
|
87
|
+
comparison = null;
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
throw err;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// --- 4. Render markdown ---
|
|
95
|
+
const markdown = renderReport(run, bundle, comparison, workspace_id);
|
|
96
|
+
// --- 5. Resolve output path ---
|
|
97
|
+
const idShort = run_id.slice(0, 8);
|
|
98
|
+
const defaultFilename = `edgegate-run-${idShort}.md`;
|
|
99
|
+
const resolvedPath = await resolveOutputPath(output_path, defaultFilename);
|
|
100
|
+
// Ensure parent directory exists
|
|
101
|
+
const parent = resolvedPath.slice(0, resolvedPath.lastIndexOf("/"));
|
|
102
|
+
if (parent) {
|
|
103
|
+
await mkdir(parent, { recursive: true });
|
|
104
|
+
}
|
|
105
|
+
// --- 6. Write the file ---
|
|
106
|
+
await writeFile(resolvedPath, markdown, "utf8");
|
|
107
|
+
// --- 7. Return result ---
|
|
108
|
+
const previewLines = markdown.split("\n").slice(0, 30).join("\n");
|
|
109
|
+
const header = `Wrote run report to ${resolvedPath}\n\n`;
|
|
110
|
+
return {
|
|
111
|
+
content: [{ type: "text", text: header + previewLines }],
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
if (err instanceof EdgeGateError) {
|
|
116
|
+
return {
|
|
117
|
+
isError: true,
|
|
118
|
+
content: [{ type: "text", text: `EdgeGate returned ${err.status}: ${err.detail}` }],
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
throw err;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// ─── Path resolution ──────────────────────────────────────────────────────────
|
|
125
|
+
async function resolveOutputPath(outputPath, defaultFilename) {
|
|
126
|
+
if (!outputPath) {
|
|
127
|
+
// Default: CWD / edgegate-run-{id-short}.md
|
|
128
|
+
return join(process.cwd(), defaultFilename);
|
|
129
|
+
}
|
|
130
|
+
// Expand ~ to home dir
|
|
131
|
+
let expanded = outputPath;
|
|
132
|
+
if (expanded.startsWith("~/")) {
|
|
133
|
+
expanded = join(homedir(), expanded.slice(2));
|
|
134
|
+
}
|
|
135
|
+
else if (expanded === "~") {
|
|
136
|
+
expanded = homedir();
|
|
137
|
+
}
|
|
138
|
+
// Resolve relative to CWD
|
|
139
|
+
const resolved = expanded.startsWith("/") ? expanded : join(process.cwd(), expanded);
|
|
140
|
+
// Check if it's an existing directory
|
|
141
|
+
try {
|
|
142
|
+
const s = await stat(resolved);
|
|
143
|
+
if (s.isDirectory()) {
|
|
144
|
+
return join(resolved, defaultFilename);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
// Doesn't exist yet — treat as file path
|
|
149
|
+
}
|
|
150
|
+
return resolved;
|
|
151
|
+
}
|
|
152
|
+
// ─── Rendering ────────────────────────────────────────────────────────────────
|
|
153
|
+
function statusBadge(status) {
|
|
154
|
+
const upper = status.toUpperCase();
|
|
155
|
+
switch (upper) {
|
|
156
|
+
case "PASSED": return "**PASSED**";
|
|
157
|
+
case "FAILED": return "**FAILED**";
|
|
158
|
+
case "RUNNING": return "_RUNNING_";
|
|
159
|
+
case "PENDING": return "_PENDING_";
|
|
160
|
+
case "ERROR": return "**ERROR**";
|
|
161
|
+
default: return `_${upper}_`;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function wallClock(run) {
|
|
165
|
+
if (!run.completed_at || !run.created_at)
|
|
166
|
+
return "(pending)";
|
|
167
|
+
const startMs = new Date(run.created_at).getTime();
|
|
168
|
+
const endMs = new Date(run.completed_at).getTime();
|
|
169
|
+
const totalS = Math.round((endMs - startMs) / 1000);
|
|
170
|
+
const m = Math.floor(totalS / 60);
|
|
171
|
+
const s = totalS % 60;
|
|
172
|
+
return m > 0 ? `${m}m ${s}s` : `${s}s`;
|
|
173
|
+
}
|
|
174
|
+
function renderReport(run, bundle, comparison, workspaceId) {
|
|
175
|
+
const lines = [];
|
|
176
|
+
// ── Header ──
|
|
177
|
+
lines.push(`# EdgeGate Run Report`);
|
|
178
|
+
lines.push(``);
|
|
179
|
+
lines.push(`**Run ID:** \`${run.id}\``);
|
|
180
|
+
lines.push(`**Pipeline:** ${run.pipeline_name} (\`${run.pipeline_id}\`)`);
|
|
181
|
+
lines.push(`**Status:** ${statusBadge(run.status)}`);
|
|
182
|
+
lines.push(`**Trigger:** ${run.trigger}`);
|
|
183
|
+
lines.push(`**Created:** ${run.created_at}`);
|
|
184
|
+
lines.push(`**Completed:** ${run.completed_at ?? "(in flight)"}`);
|
|
185
|
+
lines.push(`**Wall clock:** ${wallClock(run)}`);
|
|
186
|
+
lines.push(``);
|
|
187
|
+
// ── Model ──
|
|
188
|
+
if (run.model_artifact_id || run.hub_model_id || run.hub_job_id) {
|
|
189
|
+
lines.push(`## Model`);
|
|
190
|
+
if (run.model_artifact_id)
|
|
191
|
+
lines.push(`- Artifact ID: \`${run.model_artifact_id}\``);
|
|
192
|
+
if (run.model_filename)
|
|
193
|
+
lines.push(`- Filename: ${run.model_filename}`);
|
|
194
|
+
if (run.hub_model_id)
|
|
195
|
+
lines.push(`- AI Hub model: \`${run.hub_model_id}\``);
|
|
196
|
+
if (run.hub_job_id)
|
|
197
|
+
lines.push(`- AI Hub job: \`${run.hub_job_id}\``);
|
|
198
|
+
lines.push(``);
|
|
199
|
+
}
|
|
200
|
+
// ── In-flight guard ──
|
|
201
|
+
const isComplete = TERMINAL_STATUSES.has(run.status) && (bundle !== null || run.gates_eval !== null);
|
|
202
|
+
if (!isComplete) {
|
|
203
|
+
lines.push(`## Status`);
|
|
204
|
+
lines.push(``);
|
|
205
|
+
lines.push(`_Run not yet complete — check back._`);
|
|
206
|
+
lines.push(``);
|
|
207
|
+
if (run.error_code) {
|
|
208
|
+
lines.push(`**Error code:** ${run.error_code}`);
|
|
209
|
+
if (run.error_detail)
|
|
210
|
+
lines.push(`**Error detail:** ${run.error_detail}`);
|
|
211
|
+
lines.push(``);
|
|
212
|
+
}
|
|
213
|
+
appendFooter(lines);
|
|
214
|
+
if (comparison)
|
|
215
|
+
appendDiffSection(lines, comparison);
|
|
216
|
+
return lines.join("\n");
|
|
217
|
+
}
|
|
218
|
+
// ── Metrics ──
|
|
219
|
+
const metrics = bundle?.normalized_metrics ?? run.normalized_metrics;
|
|
220
|
+
if (metrics && Object.keys(metrics).length > 0) {
|
|
221
|
+
lines.push(`## Metrics`);
|
|
222
|
+
lines.push(`| Metric | Value |`);
|
|
223
|
+
lines.push(`|---|---|`);
|
|
224
|
+
for (const [k, v] of Object.entries(metrics)) {
|
|
225
|
+
lines.push(`| ${k} | ${v} |`);
|
|
226
|
+
}
|
|
227
|
+
lines.push(``);
|
|
228
|
+
}
|
|
229
|
+
// ── Gate Results ──
|
|
230
|
+
const gatesEval = bundle?.gates_eval ?? run.gates_eval;
|
|
231
|
+
if (gatesEval && gatesEval.gates.length > 0) {
|
|
232
|
+
lines.push(`## Gate Results`);
|
|
233
|
+
lines.push(`| Metric | Operator | Threshold | Actual | Status |`);
|
|
234
|
+
lines.push(`|---|---|---|---|---|`);
|
|
235
|
+
for (const gate of gatesEval.gates) {
|
|
236
|
+
lines.push(renderGateRow(gate));
|
|
237
|
+
}
|
|
238
|
+
lines.push(``);
|
|
239
|
+
lines.push(`**Overall verdict:** ${gatesEval.passed ? "**PASSED**" : "**FAILED**"}`);
|
|
240
|
+
lines.push(``);
|
|
241
|
+
}
|
|
242
|
+
// ── Error info ──
|
|
243
|
+
if (run.error_code) {
|
|
244
|
+
lines.push(`## Error`);
|
|
245
|
+
lines.push(`- Code: ${run.error_code}`);
|
|
246
|
+
if (run.error_detail)
|
|
247
|
+
lines.push(`- Detail: ${run.error_detail}`);
|
|
248
|
+
lines.push(``);
|
|
249
|
+
}
|
|
250
|
+
// ── Evidence Bundle ──
|
|
251
|
+
const bundleArtifactId = bundle?.bundle_artifact_id ?? run.bundle_artifact_id;
|
|
252
|
+
if (bundleArtifactId) {
|
|
253
|
+
lines.push(`## Evidence Bundle`);
|
|
254
|
+
lines.push(`- Bundle artifact ID: \`${bundleArtifactId}\``);
|
|
255
|
+
lines.push(`- Download via API: \`GET /v1/workspaces/${workspaceId}/bundles/${bundleArtifactId}/artifact-url\`` +
|
|
256
|
+
` (returns short-lived signed URL)`);
|
|
257
|
+
lines.push(``);
|
|
258
|
+
}
|
|
259
|
+
// ── Diff section ──
|
|
260
|
+
if (comparison) {
|
|
261
|
+
appendDiffSection(lines, comparison);
|
|
262
|
+
}
|
|
263
|
+
appendFooter(lines);
|
|
264
|
+
return lines.join("\n");
|
|
265
|
+
}
|
|
266
|
+
function renderGateRow(gate) {
|
|
267
|
+
const statusIcon = gate.passed ? "✓ PASSED" : "✗ FAILED";
|
|
268
|
+
return `| ${gate.metric} | ${gate.operator} | ${gate.threshold} | ${gate.actual_value} | ${statusIcon} |`;
|
|
269
|
+
}
|
|
270
|
+
function appendDiffSection(lines, comparison) {
|
|
271
|
+
const diff = comparison.diff;
|
|
272
|
+
lines.push(`## Run-vs-Baseline Diff`);
|
|
273
|
+
lines.push(``);
|
|
274
|
+
lines.push(`**Candidate:** \`${comparison.current_run_id}\` ` +
|
|
275
|
+
`(${diff.current_completed_at ?? "in flight"})`);
|
|
276
|
+
lines.push(`**Baseline:** \`${comparison.previous_run_id ?? "—"}\` ` +
|
|
277
|
+
`(${diff.previous_completed_at ?? "—"})`);
|
|
278
|
+
lines.push(``);
|
|
279
|
+
if (diff.is_baseline) {
|
|
280
|
+
lines.push(`> **NO BASELINE** — this is the first completed run in this pipeline.`);
|
|
281
|
+
lines.push(``);
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
// Metrics
|
|
285
|
+
const metricKeys = Object.keys(diff.metric_deltas).sort();
|
|
286
|
+
if (metricKeys.length > 0) {
|
|
287
|
+
lines.push(`### Metrics`);
|
|
288
|
+
lines.push(`| Metric | Baseline | Candidate | Delta |`);
|
|
289
|
+
lines.push(`|---|---|---|---|`);
|
|
290
|
+
for (const k of metricKeys) {
|
|
291
|
+
const m = diff.metric_deltas[k];
|
|
292
|
+
const pct = m.delta_pct !== null
|
|
293
|
+
? `${m.delta_pct > 0 ? "+" : ""}${m.delta_pct.toFixed(1)}%`
|
|
294
|
+
: "—";
|
|
295
|
+
const delta = m.delta !== null ? `${m.delta > 0 ? "+" : ""}${m.delta.toFixed(2)} (${pct})` : "—";
|
|
296
|
+
lines.push(`| ${k} | ${m.previous ?? "—"} | ${m.current ?? "—"} | ${delta} |`);
|
|
297
|
+
}
|
|
298
|
+
lines.push(``);
|
|
299
|
+
}
|
|
300
|
+
// Gate flips
|
|
301
|
+
if (diff.gate_flips.length > 0) {
|
|
302
|
+
lines.push(`### Gate Status`);
|
|
303
|
+
lines.push(`| Gate | Baseline | Candidate | Transition |`);
|
|
304
|
+
lines.push(`|---|---|---|---|`);
|
|
305
|
+
for (const gf of diff.gate_flips) {
|
|
306
|
+
const baseIcon = gf.previous?.passed === true ? "✓" : gf.previous?.passed === false ? "✗" : "—";
|
|
307
|
+
const candIcon = gf.current?.passed === true ? "✓" : gf.current?.passed === false ? "✗" : "—";
|
|
308
|
+
const label = gf.transition === "regressed" ? "**REGRESSION** ✓→✗"
|
|
309
|
+
: gf.transition === "improved" ? "RECOVERY ✗→✓"
|
|
310
|
+
: gf.transition;
|
|
311
|
+
lines.push(`| ${gf.metric} | ${baseIcon} | ${candIcon} | ${label} |`);
|
|
312
|
+
}
|
|
313
|
+
lines.push(``);
|
|
314
|
+
}
|
|
315
|
+
// Audit
|
|
316
|
+
if (comparison.diff_sha256) {
|
|
317
|
+
lines.push(`**Diff SHA-256:** \`${comparison.diff_sha256}\` (signed, embedded in evidence bundle)`);
|
|
318
|
+
lines.push(``);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
function appendFooter(lines) {
|
|
322
|
+
lines.push(`---`);
|
|
323
|
+
lines.push(`_Generated by edgegate-mcp@${VERSION} on ${new Date().toISOString()}_`);
|
|
324
|
+
}
|
|
325
|
+
// Export the tmpdir helper so tests can use the same default resolution logic
|
|
326
|
+
export { tmpdir as _tmpdir };
|
|
327
|
+
//# sourceMappingURL=export_run_report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export_run_report.js","sourceRoot":"","sources":["../../src/tools/export_run_report.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -89,3 +89,46 @@ export interface AuditReport {
|
|
|
89
89
|
url?: string;
|
|
90
90
|
generated_at?: string;
|
|
91
91
|
}
|
|
92
|
+
export interface MetricDelta {
|
|
93
|
+
current: number | null;
|
|
94
|
+
previous: number | null;
|
|
95
|
+
delta: number | null;
|
|
96
|
+
delta_pct: number | null;
|
|
97
|
+
}
|
|
98
|
+
export interface GateFlip {
|
|
99
|
+
metric: string;
|
|
100
|
+
/** "unchanged" | "improved" | "regressed" | "still_failing" | "new" | "removed" */
|
|
101
|
+
transition: string;
|
|
102
|
+
previous: {
|
|
103
|
+
passed: boolean | null;
|
|
104
|
+
threshold: number | null;
|
|
105
|
+
operator: string | null;
|
|
106
|
+
actual_value: number | null;
|
|
107
|
+
} | null;
|
|
108
|
+
current: {
|
|
109
|
+
passed: boolean | null;
|
|
110
|
+
threshold: number | null;
|
|
111
|
+
operator: string | null;
|
|
112
|
+
actual_value: number | null;
|
|
113
|
+
} | null;
|
|
114
|
+
}
|
|
115
|
+
export interface RunDiffPayload {
|
|
116
|
+
current_run_id: UUID;
|
|
117
|
+
previous_run_id: UUID | null;
|
|
118
|
+
current_commit: Record<string, string | null>;
|
|
119
|
+
previous_commit: Record<string, string | null> | null;
|
|
120
|
+
current_completed_at: string | null;
|
|
121
|
+
previous_completed_at: string | null;
|
|
122
|
+
metric_deltas: Record<string, MetricDelta>;
|
|
123
|
+
gate_flips: GateFlip[];
|
|
124
|
+
per_device: Record<string, Record<string, MetricDelta>> | null;
|
|
125
|
+
per_cell: unknown[] | null;
|
|
126
|
+
is_baseline: boolean;
|
|
127
|
+
}
|
|
128
|
+
export interface RunComparison {
|
|
129
|
+
current_run_id: UUID;
|
|
130
|
+
previous_run_id: UUID | null;
|
|
131
|
+
diff_sha256: string | null;
|
|
132
|
+
diff: RunDiffPayload;
|
|
133
|
+
created_at: string;
|
|
134
|
+
}
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "0.1
|
|
2
|
-
export declare const USER_AGENT = "edgegate-mcp/0.1
|
|
1
|
+
export declare const VERSION = "0.2.1";
|
|
2
|
+
export declare const USER_AGENT = "edgegate-mcp/0.2.1";
|
package/dist/version.js
CHANGED
package/package.json
CHANGED
|
@@ -12,3 +12,11 @@ The user wants to know how a run is doing.
|
|
|
12
12
|
3. Render the result. If the run is FAILED, lead with the violating gate and the actual value — don't bury it.
|
|
13
13
|
|
|
14
14
|
For PASSED runs, briefly summarize the metrics so the user has the numbers handy for a PR comment.
|
|
15
|
+
|
|
16
|
+
## "Is this run a regression?"
|
|
17
|
+
|
|
18
|
+
If the user asks whether a run is a regression, or wants to see how it compares to the previous one, call `edgegate_compare_runs` with the `run_id` (and optionally a `baseline_run_id`). The tool auto-selects the baseline from the same pipeline when no explicit baseline is given. Read the **Verdict** section of the output — REGRESSION means at least one gate flipped ✓→✗ or a lower-is-better metric increased by ≥ 25%.
|
|
19
|
+
|
|
20
|
+
## "Save" or "export" the report
|
|
21
|
+
|
|
22
|
+
If the user asks to save, export, or download the run report as a file, call `edgegate_export_run_report` with the `workspace_id`, `run_id`, and optionally an `output_path`. Pass `include_diff: true` if they also want the baseline comparison included in the file. The tool writes a complete markdown file to disk and returns the absolute path.
|