@oqtopus-team/pi-qdash 0.1.10 → 0.1.11-beta.19
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
CHANGED
|
@@ -16,7 +16,7 @@ It uses [`@oqtopus-team/qdash-client`](https://www.npmjs.com/package/@oqtopus-te
|
|
|
16
16
|
|
|
17
17
|
## Key Features
|
|
18
18
|
|
|
19
|
-
- **Read-only investigation**: dedicated tools to inspect chips, qubits, couplings, calibration task results, task knowledge (`qdash_get_task_knowledge`), executions,
|
|
19
|
+
- **Read-only investigation**: dedicated tools to inspect chips, qubits, couplings, calibration task results, task knowledge (`qdash_get_task_knowledge`), executions, and Forum posts. Secrets are redacted from tool output.
|
|
20
20
|
- **Dashboards and insights**: compact dashboards, triage overviews, target-level incident reports, degradation reports, and wiring insights.
|
|
21
21
|
- **Agent calibration workflow**: create agent sessions, submit and track agent actions, and commit/apply calibration candidates. Write operations are approval-gated.
|
|
22
22
|
- **Calibration pipelines**: compose a declarative pipeline spec from QDash's step catalog (`qdash_get_pipeline_catalog`), validate it (`qdash_plan_pipeline`), and run it as one execution (`qdash_run_pipeline`, approval-gated).
|
package/extensions/lib/links.ts
CHANGED
|
@@ -44,12 +44,10 @@ export function qdashObjectLinks(client: QDashClient, object: Record<string, unk
|
|
|
44
44
|
const executionId = firstString(object, ["execution_id", "executionId"]);
|
|
45
45
|
const chipId = firstString(object, ["chip_id", "chipId"]);
|
|
46
46
|
const postId = firstString(object, ["post_id", "forum_post_id", "id"]);
|
|
47
|
-
const issueId = firstString(object, ["issue_id"]);
|
|
48
47
|
const sessionId = firstString(object, ["session_id", "sessionId"]);
|
|
49
48
|
if (taskId) links.task_result = qdashWebUrl(client, `/task-results/${encodeURIComponent(taskId)}`);
|
|
50
49
|
if (executionId) links.execution = qdashWebUrl(client, executionPagePath(executionId, chipId));
|
|
51
50
|
if (postId) links.forum_post = qdashWebUrl(client, forumPostPagePath(postId));
|
|
52
|
-
if (issueId) links.issue = qdashWebUrl(client, `/issues/${encodeURIComponent(issueId)}`);
|
|
53
51
|
if (sessionId) links.agent_session = qdashWebUrl(client, `/agent-sessions/${encodeURIComponent(sessionId)}`);
|
|
54
52
|
return links;
|
|
55
53
|
}
|
package/extensions/qdash.ts
CHANGED
|
@@ -52,7 +52,6 @@ type QDashQueryParams = {
|
|
|
52
52
|
| "task_results"
|
|
53
53
|
| "task_result"
|
|
54
54
|
| "task_note"
|
|
55
|
-
| "task_result_issues"
|
|
56
55
|
| "qubit_latest"
|
|
57
56
|
| "qubit_history"
|
|
58
57
|
| "coupling_latest"
|
|
@@ -65,8 +64,6 @@ type QDashQueryParams = {
|
|
|
65
64
|
| "files_tree"
|
|
66
65
|
| "file_content"
|
|
67
66
|
| "git_status"
|
|
68
|
-
| "issues"
|
|
69
|
-
| "issue_knowledge"
|
|
70
67
|
| "flows"
|
|
71
68
|
| "flow"
|
|
72
69
|
| "flow_templates"
|
|
@@ -108,7 +105,6 @@ type QDashQueryParams = {
|
|
|
108
105
|
endAt?: string;
|
|
109
106
|
startFrom?: string;
|
|
110
107
|
startTo?: string;
|
|
111
|
-
isClosed?: boolean;
|
|
112
108
|
withinHours?: number;
|
|
113
109
|
limit?: number;
|
|
114
110
|
skip?: number;
|
|
@@ -294,7 +290,6 @@ const QueryAction = Type.Union([
|
|
|
294
290
|
Type.Literal("task_results"),
|
|
295
291
|
Type.Literal("task_result"),
|
|
296
292
|
Type.Literal("task_note"),
|
|
297
|
-
Type.Literal("task_result_issues"),
|
|
298
293
|
Type.Literal("qubit_latest"),
|
|
299
294
|
Type.Literal("qubit_history"),
|
|
300
295
|
Type.Literal("coupling_latest"),
|
|
@@ -307,8 +302,6 @@ const QueryAction = Type.Union([
|
|
|
307
302
|
Type.Literal("files_tree"),
|
|
308
303
|
Type.Literal("file_content"),
|
|
309
304
|
Type.Literal("git_status"),
|
|
310
|
-
Type.Literal("issues"),
|
|
311
|
-
Type.Literal("issue_knowledge"),
|
|
312
305
|
Type.Literal("flows"),
|
|
313
306
|
Type.Literal("flow"),
|
|
314
307
|
Type.Literal("flow_templates"),
|
|
@@ -371,7 +364,6 @@ const querySchema = Type.Object({
|
|
|
371
364
|
endAt: Type.Optional(Type.String()),
|
|
372
365
|
startFrom: Type.Optional(Type.String()),
|
|
373
366
|
startTo: Type.Optional(Type.String()),
|
|
374
|
-
isClosed: Type.Optional(Type.Boolean()),
|
|
375
367
|
withinHours: Type.Optional(Type.Number()),
|
|
376
368
|
limit: Type.Optional(Type.Number()),
|
|
377
369
|
skip: Type.Optional(Type.Number()),
|
|
@@ -578,25 +570,25 @@ async function buildDashboard(params: { profile?: string; configPath?: string; u
|
|
|
578
570
|
const limit = params.limit ?? 5;
|
|
579
571
|
const client = await makeClient(params);
|
|
580
572
|
const chipId = await defaultChipId(client, params.chipId);
|
|
581
|
-
const [chips,
|
|
573
|
+
const [chips, openThreads, recentExecutions, failedTaskResults, provenanceStats] = await Promise.allSettled([
|
|
582
574
|
client.listChips(),
|
|
583
|
-
client.
|
|
575
|
+
client.listForumPosts({ chipId, status: "open", limit }),
|
|
584
576
|
client.api.listExecutions({ chip_id: chipId, limit }),
|
|
585
577
|
client.api.listTaskResults({ chip_id: chipId, status: "failed", limit }),
|
|
586
578
|
client.getProvenanceStats(),
|
|
587
579
|
]);
|
|
588
580
|
const value = <T>(result: PromiseSettledResult<T>): T | { error: string } => result.status === "fulfilled" ? result.value : { error: result.reason instanceof Error ? result.reason.message : String(result.reason) };
|
|
589
|
-
const
|
|
581
|
+
const threadsPayload = value(openThreads);
|
|
590
582
|
const executionsPayload = value(recentExecutions);
|
|
591
583
|
const failedTasksPayload = value(failedTaskResults);
|
|
592
584
|
return {
|
|
593
585
|
context: { ...currentContext, profile: params.profile ?? currentContext.profile, chipId },
|
|
594
586
|
chips: value(chips),
|
|
595
|
-
|
|
596
|
-
count: payloadTotal(
|
|
597
|
-
shown: arrayFromPayload(
|
|
598
|
-
items: compactItems(
|
|
599
|
-
raw:
|
|
587
|
+
openThreads: {
|
|
588
|
+
count: payloadTotal(threadsPayload),
|
|
589
|
+
shown: arrayFromPayload(threadsPayload).length,
|
|
590
|
+
items: compactItems(threadsPayload, ["number", "title", "category", "target_type", "target_id", "created_at"], limit),
|
|
591
|
+
raw: threadsPayload,
|
|
600
592
|
},
|
|
601
593
|
recentExecutions: {
|
|
602
594
|
count: payloadTotal(executionsPayload),
|
|
@@ -624,14 +616,14 @@ function dashboardLines(dashboard: Awaited<ReturnType<typeof buildDashboard>>, c
|
|
|
624
616
|
const success = (text: string) => color ? ansi("32", text) : text;
|
|
625
617
|
const warning = (text: string) => color ? ansi("33", text) : text;
|
|
626
618
|
const error = (text: string) => color ? ansi("31", text) : text;
|
|
627
|
-
const
|
|
619
|
+
const threadCount = dashboard.openThreads.count > 0 ? warning(String(dashboard.openThreads.count)) : success(String(dashboard.openThreads.count));
|
|
628
620
|
const failedCount = dashboard.failedTaskResults.count > 0 ? error(`${dashboard.failedTaskResults.shown}/${dashboard.failedTaskResults.count}`) : success(`${dashboard.failedTaskResults.shown}/${dashboard.failedTaskResults.count}`);
|
|
629
621
|
return boxed("QDash Harness", [
|
|
630
622
|
`${muted("profile")} ${accent(profile)} ${muted("chip")} ${accent(dashboard.context.chipId)} ${muted("session")} ${dim(session)}`,
|
|
631
|
-
`${muted("chips")} ${success(String(chips.length))} ${muted("open
|
|
623
|
+
`${muted("chips")} ${success(String(chips.length))} ${muted("open threads")} ${threadCount} ${muted("executions")} ${accent(`${dashboard.recentExecutions.shown}/${dashboard.recentExecutions.count}`)} ${muted("failed tasks")} ${failedCount}`,
|
|
632
624
|
"",
|
|
633
|
-
accent("Open
|
|
634
|
-
...(dashboard.
|
|
625
|
+
accent("Open Forum threads"),
|
|
626
|
+
...(dashboard.openThreads.items.length > 0 ? dashboard.openThreads.items.map((item, index) => ` ${warning(formatItem(item, `thread-${index + 1}`))}`) : [` ${dim("none")}`]),
|
|
635
627
|
"",
|
|
636
628
|
accent("Recent executions"),
|
|
637
629
|
...(dashboard.recentExecutions.items.length > 0 ? dashboard.recentExecutions.items.map((item, index) => ` ${muted(formatItem(item, `exec-${index + 1}`))}`) : [` ${dim("none")}`]),
|
|
@@ -645,7 +637,7 @@ function styledDashboardLines(dashboard: Awaited<ReturnType<typeof buildDashboar
|
|
|
645
637
|
const chips = arrayFromPayload(dashboard.chips);
|
|
646
638
|
const profile = dashboard.context.profile ?? "env/default";
|
|
647
639
|
const session = dashboard.context.agentSessionId ? shortId(dashboard.context.agentSessionId) : "none";
|
|
648
|
-
const
|
|
640
|
+
const threadColor = dashboard.openThreads.count > 0 ? "warning" : "success";
|
|
649
641
|
const failedColor = dashboard.failedTaskResults.count > 0 ? "error" : "success";
|
|
650
642
|
return [
|
|
651
643
|
theme.fg("accent", theme.bold("QDash Harness")),
|
|
@@ -653,14 +645,14 @@ function styledDashboardLines(dashboard: Awaited<ReturnType<typeof buildDashboar
|
|
|
653
645
|
"",
|
|
654
646
|
[
|
|
655
647
|
`${theme.fg("muted", "chips")} ${theme.fg("success", String(chips.length))}`,
|
|
656
|
-
`${theme.fg("muted", "open
|
|
648
|
+
`${theme.fg("muted", "open threads")} ${theme.fg(threadColor, String(dashboard.openThreads.count))}`,
|
|
657
649
|
`${theme.fg("muted", "executions")} ${theme.fg("accent", `${dashboard.recentExecutions.shown}/${dashboard.recentExecutions.count}`)}`,
|
|
658
650
|
`${theme.fg("muted", "failed tasks")} ${theme.fg(failedColor, `${dashboard.failedTaskResults.shown}/${dashboard.failedTaskResults.count}`)}`,
|
|
659
651
|
].join(" "),
|
|
660
652
|
"",
|
|
661
|
-
theme.fg("borderAccent", "Open
|
|
662
|
-
...(dashboard.
|
|
663
|
-
? dashboard.
|
|
653
|
+
theme.fg("borderAccent", "Open Forum threads"),
|
|
654
|
+
...(dashboard.openThreads.items.length > 0
|
|
655
|
+
? dashboard.openThreads.items.map((item, index) => ` ${theme.fg("warning", formatItem(item, `thread-${index + 1}`))}`)
|
|
664
656
|
: [` ${theme.fg("dim", "none")}`]),
|
|
665
657
|
"",
|
|
666
658
|
theme.fg("borderAccent", "Recent executions"),
|
|
@@ -871,7 +863,6 @@ type TargetOperationsReport = {
|
|
|
871
863
|
latestTarget: unknown;
|
|
872
864
|
recentResults: Record<string, unknown>[];
|
|
873
865
|
failures: Record<string, unknown>[];
|
|
874
|
-
issues: Record<string, unknown>[];
|
|
875
866
|
forumPosts: Record<string, unknown>[];
|
|
876
867
|
recommendation: RecommendedNextAction | null;
|
|
877
868
|
};
|
|
@@ -922,19 +913,12 @@ async function buildTargetOperationsReport(params: { profile?: string; configPat
|
|
|
922
913
|
const end = new Date();
|
|
923
914
|
const start = new Date(end.getTime() - (params.withinHours ?? 168) * 3600_000);
|
|
924
915
|
const limit = Math.max(1, Math.min(100, params.limit ?? 10));
|
|
925
|
-
const [latestTarget, results,
|
|
916
|
+
const [latestTarget, results, forumPosts] = await Promise.all([
|
|
926
917
|
kind === "qubit" ? client.getChipQubit(chipId, id) : client.getChipCoupling(chipId, id),
|
|
927
918
|
client.listTaskResults({ chipId, qid, couplingId, startAt: start.toISOString(), endAt: end.toISOString(), limit }),
|
|
928
|
-
client.api.listIssues({ is_closed: false, limit: 100 }),
|
|
929
919
|
client.listForumPosts({ chipId, status: "open", targetType: kind, targetId: id, limit }),
|
|
930
920
|
]);
|
|
931
921
|
const recentResults = arrayFromPayload(results).filter((item): item is Record<string, unknown> => Boolean(item) && typeof item === "object");
|
|
932
|
-
const taskIds = new Set(recentResults.map((item) => firstString(item, ["task_id", "taskId"])).filter((item): item is string => Boolean(item)));
|
|
933
|
-
const targetIssues = arrayFromPayload(issues).filter((item): item is Record<string, unknown> => {
|
|
934
|
-
if (!item || typeof item !== "object") return false;
|
|
935
|
-
const issue = item as Record<string, unknown>;
|
|
936
|
-
return (firstString(issue, ["task_id", "taskId"]) && taskIds.has(firstString(issue, ["task_id", "taskId"])!)) || targetFromText(firstString(issue, ["title", "summary"]) ?? "") === label;
|
|
937
|
-
});
|
|
938
922
|
const summary: RecentCalibrationSummary = { context: { profile: params.profile ?? currentContext.profile, chipId, webBaseUrl: qdashWebBaseUrl(client) }, items: recentResults, groups: [] };
|
|
939
923
|
const groups = new Map<string, Record<string, unknown>[]>();
|
|
940
924
|
groups.set(label, recentResults);
|
|
@@ -946,7 +930,6 @@ async function buildTargetOperationsReport(params: { profile?: string; configPat
|
|
|
946
930
|
latestTarget,
|
|
947
931
|
recentResults,
|
|
948
932
|
failures: recentResults.filter((item) => firstString(item, ["status"])?.toLowerCase() === "failed"),
|
|
949
|
-
issues: targetIssues,
|
|
950
933
|
forumPosts: forumPostsFromPayload(forumPosts),
|
|
951
934
|
recommendation,
|
|
952
935
|
};
|
|
@@ -965,7 +948,7 @@ function targetOperationsReportLines(report: TargetOperationsReport, color = fal
|
|
|
965
948
|
};
|
|
966
949
|
return boxed(`QDash Target Report ${report.target.label}`, [
|
|
967
950
|
`${muted("profile")} ${accent(report.context.profile ?? "env/default")} ${muted("chip")} ${accent(report.context.chipId)}`,
|
|
968
|
-
`${muted("results")} ${report.recentResults.length} ${muted("failures")} ${report.failures.length ? error(String(report.failures.length)) : "0"} ${muted("
|
|
951
|
+
`${muted("results")} ${report.recentResults.length} ${muted("failures")} ${report.failures.length ? error(String(report.failures.length)) : "0"} ${muted("forum")} ${report.forumPosts.length ? warn(String(report.forumPosts.length)) : "0"}`,
|
|
969
952
|
"",
|
|
970
953
|
accent("Recent results"),
|
|
971
954
|
...(report.recentResults.length ? report.recentResults.slice(0, 10).map(resultLine) : [` ${muted("none")}`]),
|
|
@@ -1097,7 +1080,6 @@ type CalibrationValidation = {
|
|
|
1097
1080
|
status: "passed" | "failed" | "needs_review" | "unknown";
|
|
1098
1081
|
gates: Array<{ name: string; passed: boolean; detail: string }>;
|
|
1099
1082
|
figurePaths: string[];
|
|
1100
|
-
issues: Record<string, unknown>[];
|
|
1101
1083
|
comparison?: unknown;
|
|
1102
1084
|
next: string;
|
|
1103
1085
|
links: Record<string, string>;
|
|
@@ -1109,8 +1091,6 @@ async function buildCalibrationValidation(params: { profile?: string; configPath
|
|
|
1109
1091
|
const task = await client.getTaskResult(params.taskId) as unknown as Record<string, unknown>;
|
|
1110
1092
|
const status = firstString(task, ["status"])?.toLowerCase() ?? "unknown";
|
|
1111
1093
|
const figurePaths = taskFigurePaths(task, 20);
|
|
1112
|
-
const issuesPayload = await client.api.getTaskResultIssues(params.taskId).catch(() => []);
|
|
1113
|
-
const issues = arrayFromPayload(issuesPayload).filter((item): item is Record<string, unknown> => Boolean(item) && typeof item === "object");
|
|
1114
1094
|
const afterExecutionId = firstString(task, ["execution_id", "executionId"]);
|
|
1115
1095
|
const comparison = params.beforeExecutionId && afterExecutionId
|
|
1116
1096
|
? await client.compareExecutions(params.beforeExecutionId, afterExecutionId).catch(() => undefined)
|
|
@@ -1119,18 +1099,17 @@ async function buildCalibrationValidation(params: { profile?: string; configPath
|
|
|
1119
1099
|
const changedParameters = comparisonRecord ? firstNumber(comparisonRecord, ["changed_count"]) ?? (Array.isArray(comparisonRecord.changed_parameters) ? (comparisonRecord.changed_parameters as unknown[]).length : undefined) : undefined;
|
|
1120
1100
|
const gates = [
|
|
1121
1101
|
{ name: "task completed", passed: ["completed", "success", "succeeded"].includes(status), detail: status },
|
|
1122
|
-
{ name: "no task issues", passed: issues.length === 0, detail: issues.length ? `${issues.length} issue(s) require review` : "none" },
|
|
1123
1102
|
{ name: "figures available", passed: figurePaths.length > 0, detail: figurePaths.length ? `${figurePaths.length} figure(s)` : "no figures attached" },
|
|
1124
1103
|
...(params.beforeExecutionId ? [{ name: "execution comparison", passed: comparison !== undefined, detail: comparison === undefined ? "comparison unavailable" : `${changedParameters ?? 0} changed parameter(s)` }] : []),
|
|
1125
1104
|
];
|
|
1126
|
-
const
|
|
1105
|
+
const figuresGate = gates.find((gate) => gate.name === "figures available");
|
|
1127
1106
|
const comparisonGate = gates.find((gate) => gate.name === "execution comparison");
|
|
1128
|
-
const validationStatus: CalibrationValidation["status"] = !gates[0].passed ? "failed" :
|
|
1107
|
+
const validationStatus: CalibrationValidation["status"] = !gates[0].passed ? "failed" : figuresGate?.passed === false || comparisonGate?.passed === false ? "needs_review" : "passed";
|
|
1129
1108
|
const next = validationStatus === "passed"
|
|
1130
1109
|
? "Inspect candidates and ask for explicit confirmation before commit/apply. Preserve evidence in the target Forum thread when appropriate."
|
|
1131
1110
|
: validationStatus === "failed"
|
|
1132
1111
|
? "Stop the recovery sequence; inspect the task message and figures before another operational action."
|
|
1133
|
-
: "Review the
|
|
1112
|
+
: "Review the figures and execution comparison before treating this result as authoritative.";
|
|
1134
1113
|
return {
|
|
1135
1114
|
context: { profile: params.profile ?? currentContext.profile, chipId: firstString(task, ["chip_id", "chipId"]) ?? currentContext.chipId, webBaseUrl: qdashWebBaseUrl(client) },
|
|
1136
1115
|
taskId: params.taskId,
|
|
@@ -1138,7 +1117,6 @@ async function buildCalibrationValidation(params: { profile?: string; configPath
|
|
|
1138
1117
|
status: validationStatus,
|
|
1139
1118
|
gates,
|
|
1140
1119
|
figurePaths,
|
|
1141
|
-
issues,
|
|
1142
1120
|
comparison,
|
|
1143
1121
|
next,
|
|
1144
1122
|
links: qdashObjectLinks(client, task),
|
|
@@ -1240,9 +1218,8 @@ async function buildDashboardInsights(params: { profile?: string; configPath?: s
|
|
|
1240
1218
|
const limit = params.limit ?? 30;
|
|
1241
1219
|
const end = new Date();
|
|
1242
1220
|
const start = new Date(end.getTime() - (params.withinHours ?? 168) * 3600_000);
|
|
1243
|
-
const [forums,
|
|
1221
|
+
const [forums, failed, metrics] = await Promise.allSettled([
|
|
1244
1222
|
client.listForumPosts({ chipId, status: "open", limit }),
|
|
1245
|
-
client.api.listIssues({ is_closed: false, limit }),
|
|
1246
1223
|
client.api.listTaskResults({ chip_id: chipId, status: "failed", start_from: start.toISOString(), start_to: end.toISOString(), limit }),
|
|
1247
1224
|
client.getChipMetrics(chipId),
|
|
1248
1225
|
]);
|
|
@@ -1260,13 +1237,6 @@ async function buildDashboardInsights(params: { profile?: string; configPath?: s
|
|
|
1260
1237
|
addEvidence(insightMap, target, severity, title, `forum: ${title}`, "Open forum context should be reviewed before calibration changes.", qdashObjectLinks(client, post));
|
|
1261
1238
|
}
|
|
1262
1239
|
|
|
1263
|
-
for (const issue of arrayFromPayload(value(issues)).filter((item): item is Record<string, unknown> => Boolean(item) && typeof item === "object")) {
|
|
1264
|
-
const title = firstString(issue, ["title", "summary"]) ?? "open issue";
|
|
1265
|
-
const target = targetFromText(title) ?? firstString(issue, ["qid"]);
|
|
1266
|
-
if (!target) continue;
|
|
1267
|
-
addEvidence(insightMap, target.startsWith("q") || target.startsWith("c") ? target : `q${target}`, "warning", title, `open issue: ${title}`, "Resolve or account for the open issue before trusting metrics.", qdashObjectLinks(client, issue));
|
|
1268
|
-
}
|
|
1269
|
-
|
|
1270
1240
|
for (const task of arrayFromPayload(value(failed)).filter((item): item is Record<string, unknown> => Boolean(item) && typeof item === "object")) {
|
|
1271
1241
|
const target = calibrationTarget(task);
|
|
1272
1242
|
const taskName = firstString(task, ["task_name", "name"]) ?? "task";
|
|
@@ -1465,7 +1435,6 @@ async function executeQuery(params: QDashQueryParams) {
|
|
|
1465
1435
|
case "task_results": return rawGet(client, "/task-results", { chip_id: params.chipId, task_name: params.taskName, qid: params.qid, coupling_id: params.couplingId, execution_id: params.executionId, username: params.username, status: params.status, start_from: params.startFrom ?? params.startAt, start_to: params.startTo ?? params.endAt, message_contains: params.messageContains, limit: params.limit, skip: params.skip });
|
|
1466
1436
|
case "task_result": return client.getTaskResult(requireValue(params.taskId, "taskId"));
|
|
1467
1437
|
case "task_note": return client.api.getTaskNote(requireValue(params.taskId, "taskId"));
|
|
1468
|
-
case "task_result_issues": return client.api.getTaskResultIssues(requireValue(params.taskId, "taskId"));
|
|
1469
1438
|
case "qubit_latest": return client.api.getLatestQubitTaskResults({ chip_id: await defaultChipId(client, params.chipId), task: requireValue(params.task ?? params.taskName, "task") });
|
|
1470
1439
|
case "qubit_history": return rawGet(client, `/task-results/qubits/${pathPart(requireValue(params.qid, "qid"))}/history`, { chip_id: await defaultChipId(client, params.chipId), task: requireValue(params.task ?? params.taskName, "task"), date: requireValue(params.date, "date") });
|
|
1471
1440
|
case "coupling_latest": return client.api.getLatestCouplingTaskResults({ chip_id: await defaultChipId(client, params.chipId), task: requireValue(params.task ?? params.taskName, "task") });
|
|
@@ -1478,8 +1447,6 @@ async function executeQuery(params: QDashQueryParams) {
|
|
|
1478
1447
|
case "files_tree": return client.getFilesTree();
|
|
1479
1448
|
case "file_content": return client.getFileContent(requireValue(params.path, "path"));
|
|
1480
1449
|
case "git_status": return client.getGitStatus();
|
|
1481
|
-
case "issues": return client.api.listIssues({ task_id: params.taskId, is_closed: params.isClosed, limit: params.limit, skip: params.skip });
|
|
1482
|
-
case "issue_knowledge": return client.api.listIssueKnowledge({ status: params.status, task_name: params.taskName, limit: params.limit, skip: params.skip });
|
|
1483
1450
|
case "flows": return client.listFlows();
|
|
1484
1451
|
case "flow": return client.getFlow(requireValue(params.flowName, "flowName"));
|
|
1485
1452
|
case "flow_templates": return client.listFlowTemplates();
|
|
@@ -1532,7 +1499,7 @@ export default function qdashExtension(pi: ExtensionAPI) {
|
|
|
1532
1499
|
description: "Run common read-only QDash queries via @oqtopus-team/qdash-client.",
|
|
1533
1500
|
promptSnippet: "Query QDash chips, cryostats, cooldown wiring, metrics, tasks, files, flows, executions, projects, forum posts, and provenance",
|
|
1534
1501
|
promptGuidelines: [
|
|
1535
|
-
"Prefer dedicated QDash tools such as qdash_list_chips, qdash_get_chip_metrics, qdash_list_task_results,
|
|
1502
|
+
"Prefer dedicated QDash tools such as qdash_list_chips, qdash_get_chip_metrics, qdash_list_task_results, qdash_list_forum_posts, qdash_list_flows, and qdash_list_executions when they match the task.",
|
|
1536
1503
|
"Use qdash_query for read-only QDash data access not covered by a dedicated tool, instead of curl or scraping the UI.",
|
|
1537
1504
|
"Ask for confirmation before using operational or write APIs; qdash_query intentionally exposes read-only operations only.",
|
|
1538
1505
|
"Never expose QDash tokens, passwords, or Cloudflare Access secrets.",
|
|
@@ -1808,15 +1775,6 @@ export default function qdashExtension(pi: ExtensionAPI) {
|
|
|
1808
1775
|
parameters: Type.Object({ ...connectionParams, taskId: Type.String() }),
|
|
1809
1776
|
});
|
|
1810
1777
|
|
|
1811
|
-
registerQueryTool({
|
|
1812
|
-
name: "qdash_list_issues",
|
|
1813
|
-
label: "QDash List Issues",
|
|
1814
|
-
description: "List QDash issues with optional task/closed filters.",
|
|
1815
|
-
promptSnippet: "List QDash issues",
|
|
1816
|
-
action: "issues",
|
|
1817
|
-
parameters: Type.Object({ ...connectionParams, taskId: Type.Optional(Type.String()), isClosed: Type.Optional(Type.Boolean()), limit: Type.Optional(Type.Number()), skip: Type.Optional(Type.Number()) }),
|
|
1818
|
-
});
|
|
1819
|
-
|
|
1820
1778
|
registerQueryTool({
|
|
1821
1779
|
name: "qdash_list_flows",
|
|
1822
1780
|
label: "QDash List Flows",
|
|
@@ -2094,21 +2052,24 @@ export default function qdashExtension(pi: ExtensionAPI) {
|
|
|
2094
2052
|
pi.registerTool({
|
|
2095
2053
|
name: "qdash_execute_agent_action",
|
|
2096
2054
|
label: "QDash Execute Agent Action",
|
|
2097
|
-
description: "
|
|
2098
|
-
promptSnippet: "
|
|
2099
|
-
promptGuidelines: [
|
|
2055
|
+
description: "Run an authorized QDash agent action's task the way the Task Workbench runs it: inputs from the current calibration state, outputs staged for the candidate gate. This is a write operation and requires confirmation.",
|
|
2056
|
+
promptSnippet: "Run a QDash agent action's task after explicit user confirmation",
|
|
2057
|
+
promptGuidelines: [
|
|
2058
|
+
"Use qdash_execute_agent_action only after the user confirms the action, its task, target, and overrides.",
|
|
2059
|
+
"The task runs on the current calibration state; pass sourceExecutionId only to record which execution the action was decided from.",
|
|
2060
|
+
],
|
|
2100
2061
|
parameters: Type.Object({
|
|
2101
2062
|
...connectionParams,
|
|
2102
2063
|
sessionId: Type.String(),
|
|
2103
2064
|
actionId: Type.String(),
|
|
2104
|
-
sourceExecutionId: Type.String(),
|
|
2065
|
+
sourceExecutionId: Type.Optional(Type.String({ description: "Execution the action was decided from, recorded for provenance. The run does not restore it." })),
|
|
2105
2066
|
updateParams: Type.Optional(Type.Boolean()),
|
|
2106
2067
|
reconfigure: Type.Optional(Type.Boolean()),
|
|
2107
2068
|
confirmWrite: Type.Optional(Type.Boolean()),
|
|
2108
2069
|
}),
|
|
2109
|
-
async execute(_toolCallId, params: ConfirmableParams & { profile?: string; configPath?: string; useEnv?: boolean; sessionId: string; actionId: string; sourceExecutionId
|
|
2070
|
+
async execute(_toolCallId, params: ConfirmableParams & { profile?: string; configPath?: string; useEnv?: boolean; sessionId: string; actionId: string; sourceExecutionId?: string; updateParams?: boolean; reconfigure?: boolean }, _signal, _onUpdate, ctx) {
|
|
2110
2071
|
if (!params.confirmWrite) {
|
|
2111
|
-
if (!ctx.hasUI || !(await ctx.ui.confirm("Execute QDash agent action?", `
|
|
2072
|
+
if (!ctx.hasUI || !(await ctx.ui.confirm("Execute QDash agent action?", `Run action '${params.actionId}' on the current calibration state? Outputs stay staged until a candidate is committed.`))) {
|
|
2112
2073
|
throw new Error("qdash_execute_agent_action requires explicit confirmation");
|
|
2113
2074
|
}
|
|
2114
2075
|
}
|
|
@@ -2753,7 +2714,7 @@ export default function qdashExtension(pi: ExtensionAPI) {
|
|
|
2753
2714
|
pi.registerTool({
|
|
2754
2715
|
name: "qdash_validate_calibration",
|
|
2755
2716
|
label: "QDash Validate Calibration",
|
|
2756
|
-
description: "Validate a calibration task result with explicit gates for status
|
|
2717
|
+
description: "Validate a calibration task result with explicit gates for status and figures. Read-only; does not commit or apply candidates.",
|
|
2757
2718
|
promptSnippet: "Validate a QDash calibration result before continuing or committing parameters",
|
|
2758
2719
|
promptGuidelines: [
|
|
2759
2720
|
"Use qdash_validate_calibration after an operational task completes or fails.",
|
|
@@ -2807,7 +2768,7 @@ export default function qdashExtension(pi: ExtensionAPI) {
|
|
|
2807
2768
|
pi.registerTool({
|
|
2808
2769
|
name: "qdash_target_report",
|
|
2809
2770
|
label: "QDash Target Report",
|
|
2810
|
-
description: "Build a read-only operational report for one qubit or coupling, correlating recent results, failures,
|
|
2771
|
+
description: "Build a read-only operational report for one qubit or coupling, correlating recent results, failures, Forum context, and a safe next-action recommendation.",
|
|
2811
2772
|
promptSnippet: "Inspect one QDash qubit or coupling as an operational incident report",
|
|
2812
2773
|
promptGuidelines: [
|
|
2813
2774
|
"Use qdash_target_report before calibration changes when a specific qubit or coupling is under investigation.",
|
|
@@ -2836,7 +2797,7 @@ export default function qdashExtension(pi: ExtensionAPI) {
|
|
|
2836
2797
|
name: "qdash_dashboard",
|
|
2837
2798
|
label: "QDash Dashboard",
|
|
2838
2799
|
description: "Build a compact read-only QDash dashboard for the current profile/chip context.",
|
|
2839
|
-
promptSnippet: "Show QDash harness dashboard: chips, open
|
|
2800
|
+
promptSnippet: "Show QDash harness dashboard: chips, open Forum threads, recent executions, failed task results, and provenance stats",
|
|
2840
2801
|
promptGuidelines: [
|
|
2841
2802
|
"Use qdash_dashboard when the user asks for QDash status, overview, triage, or a dashboard.",
|
|
2842
2803
|
"Summarize dashboard data instead of dumping large raw payloads.",
|
|
@@ -2860,9 +2821,9 @@ export default function qdashExtension(pi: ExtensionAPI) {
|
|
|
2860
2821
|
pi.registerTool({
|
|
2861
2822
|
name: "qdash_dashboard_insights",
|
|
2862
2823
|
label: "QDash Dashboard Insights",
|
|
2863
|
-
description: "Correlate dashboard metrics, open
|
|
2824
|
+
description: "Correlate dashboard metrics, open Forum posts, and failed tasks into target-level insights with links.",
|
|
2864
2825
|
promptSnippet: "Generate QDash target insights from dashboard metrics, forum links, notes, and failed tasks",
|
|
2865
|
-
promptGuidelines: ["Use qdash_dashboard_insights when the user asks for insights across dashboard metrics, target summaries,
|
|
2826
|
+
promptGuidelines: ["Use qdash_dashboard_insights when the user asks for insights across dashboard metrics, target summaries, Forum posts, or anomalous targets."],
|
|
2866
2827
|
parameters: Type.Object({
|
|
2867
2828
|
...connectionParams,
|
|
2868
2829
|
chipId: Type.Optional(Type.String()),
|
|
@@ -2907,8 +2868,8 @@ export default function qdashExtension(pi: ExtensionAPI) {
|
|
|
2907
2868
|
pi.registerTool({
|
|
2908
2869
|
name: "qdash_investigate",
|
|
2909
2870
|
label: "QDash Investigate",
|
|
2910
|
-
description: "Run a read-only natural-language-friendly QDash investigation combining recent calibration results, target history, failures,
|
|
2911
|
-
promptSnippet: "Investigate a QDash target or recent calibration by correlating results, figures,
|
|
2871
|
+
description: "Run a read-only natural-language-friendly QDash investigation combining recent calibration results, target history, failures, Forum context, and safe recommendations.",
|
|
2872
|
+
promptSnippet: "Investigate a QDash target or recent calibration by correlating results, figures, and Forum context",
|
|
2912
2873
|
promptGuidelines: [
|
|
2913
2874
|
"Use qdash_investigate when the user asks to investigate, compare, or understand a QDash target or recent calibration in natural language.",
|
|
2914
2875
|
"This tool is read-only; do not execute tasks or change parameters based on its recommendation without explicit confirmation.",
|
|
@@ -2985,19 +2946,19 @@ export default function qdashExtension(pi: ExtensionAPI) {
|
|
|
2985
2946
|
pi.registerTool({
|
|
2986
2947
|
name: "qdash_triage_overview",
|
|
2987
2948
|
label: "QDash Triage Overview",
|
|
2988
|
-
description: "Create a read-only triage overview from open
|
|
2989
|
-
promptSnippet: "Summarize QDash open
|
|
2949
|
+
description: "Create a read-only triage overview from open Forum threads and failed task results for the current QDash context.",
|
|
2950
|
+
promptSnippet: "Summarize QDash open Forum threads and failed task results for triage",
|
|
2990
2951
|
promptGuidelines: ["Use qdash_triage_overview when the user asks what to investigate next in QDash."],
|
|
2991
2952
|
parameters: Type.Object({ ...connectionParams, ...chipScopedParams, limit: Type.Optional(Type.Number()), color: Type.Optional(Type.Boolean({ description: "Emit ANSI colors in text output for terminal display." })) }),
|
|
2992
2953
|
async execute(_toolCallId, params: { profile?: string; configPath?: string; useEnv?: boolean; chipId?: string; limit?: number; color?: boolean }) {
|
|
2993
2954
|
const dashboard = await buildDashboard(params);
|
|
2994
2955
|
const triage = {
|
|
2995
2956
|
context: dashboard.context,
|
|
2996
|
-
|
|
2957
|
+
openThreads: dashboard.openThreads,
|
|
2997
2958
|
failedTaskResults: dashboard.failedTaskResults,
|
|
2998
2959
|
suggestedFocus: [
|
|
2999
2960
|
dashboard.failedTaskResults.count > 0 ? "Review failed task results first." : undefined,
|
|
3000
|
-
dashboard.
|
|
2961
|
+
dashboard.openThreads.count > 0 ? "Review open Forum threads and correlate with recent executions." : undefined,
|
|
3001
2962
|
].filter(Boolean),
|
|
3002
2963
|
};
|
|
3003
2964
|
const accent = (text: string) => params.color ? ansi("1;36", text) : text;
|
|
@@ -3005,7 +2966,7 @@ export default function qdashExtension(pi: ExtensionAPI) {
|
|
|
3005
2966
|
const success = (text: string) => params.color ? ansi("32", text) : text;
|
|
3006
2967
|
const error = (text: string) => params.color ? ansi("31", text) : text;
|
|
3007
2968
|
const lines = boxed("QDash Triage", [
|
|
3008
|
-
`open
|
|
2969
|
+
`open threads ${triage.openThreads.count > 0 ? ansi("33", String(triage.openThreads.count)) : success(String(triage.openThreads.count))}`,
|
|
3009
2970
|
`failed tasks ${triage.failedTaskResults.count > 0 ? error(`${triage.failedTaskResults.shown}/${triage.failedTaskResults.count}`) : success(`${triage.failedTaskResults.shown}/${triage.failedTaskResults.count}`)}`,
|
|
3010
2971
|
"",
|
|
3011
2972
|
accent("Suggested focus"),
|
|
@@ -3017,11 +2978,11 @@ export default function qdashExtension(pi: ExtensionAPI) {
|
|
|
3017
2978
|
return toTextToolResult(lines.join("\n"), triage, { tool: "qdash_triage_overview" });
|
|
3018
2979
|
},
|
|
3019
2980
|
renderResult(result, _options, theme) {
|
|
3020
|
-
const data = (result.details as { data?: {
|
|
2981
|
+
const data = (result.details as { data?: { openThreads?: { count?: number }; failedTaskResults?: { count?: number }; suggestedFocus?: string[] } } | undefined)?.data;
|
|
3021
2982
|
if (!data) return new Text(theme.fg("dim", "QDash triage unavailable"), 0, 0);
|
|
3022
2983
|
const lines = [
|
|
3023
2984
|
theme.fg("accent", theme.bold("QDash Triage")),
|
|
3024
|
-
`${theme.fg("muted", "open
|
|
2985
|
+
`${theme.fg("muted", "open threads")} ${theme.fg((data.openThreads?.count ?? 0) > 0 ? "warning" : "success", String(data.openThreads?.count ?? 0))}`,
|
|
3025
2986
|
`${theme.fg("muted", "failed tasks")} ${theme.fg((data.failedTaskResults?.count ?? 0) > 0 ? "error" : "success", String(data.failedTaskResults?.count ?? 0))}`,
|
|
3026
2987
|
"",
|
|
3027
2988
|
theme.fg("borderAccent", "Suggested focus"),
|
|
@@ -3293,7 +3254,7 @@ export default function qdashExtension(pi: ExtensionAPI) {
|
|
|
3293
3254
|
const dashboard = await buildDashboard({ limit: Number.isFinite(limit) ? limit : undefined });
|
|
3294
3255
|
refreshContextUi(ctx);
|
|
3295
3256
|
ctx.ui.setWidget("qdash", (_tui, theme) => dashboardComponent(dashboard, theme));
|
|
3296
|
-
ctx.ui.notify(`QDash dashboard updated: ${dashboard.
|
|
3257
|
+
ctx.ui.notify(`QDash dashboard updated: ${dashboard.openThreads.count} open Forum threads, ${dashboard.failedTaskResults.count} failed tasks`, "info");
|
|
3297
3258
|
},
|
|
3298
3259
|
});
|
|
3299
3260
|
|
|
@@ -250,10 +250,12 @@ export function registerCalibrationPipelineTools(pi: Pick<ExtensionAPI, "registe
|
|
|
250
250
|
pi.registerTool({
|
|
251
251
|
name: "qdash_run_pipeline",
|
|
252
252
|
label: "QDash Run Pipeline",
|
|
253
|
-
description: "Run a calibration pipeline spec as one QDash execution. Confirmation-gated write operation. Validate with qdash_plan_pipeline first; the spec runs exactly as validated.",
|
|
253
|
+
description: "Run a calibration pipeline spec as one QDash execution. Completed tasks write their calibrated parameters to QDash directly, without an agent candidate gate. Confirmation-gated write operation. Validate with qdash_plan_pipeline first; the spec runs exactly as validated.",
|
|
254
254
|
promptSnippet: "Run a validated QDash calibration pipeline spec",
|
|
255
255
|
promptGuidelines: [
|
|
256
256
|
"Use qdash_run_pipeline only for a spec that qdash_plan_pipeline reported valid.",
|
|
257
|
+
"Tell the user before running that completed tasks update calibrated parameters in QDash directly.",
|
|
258
|
+
"Do not use qdash_run_pipeline to run a task an agent session action failed to run; that skips the session's candidate gate. Report the failure instead.",
|
|
257
259
|
"After it starts, follow the execution with qdash_wait_execution and summarize failed targets.",
|
|
258
260
|
],
|
|
259
261
|
parameters: Type.Object({
|
|
@@ -265,7 +267,7 @@ export function registerCalibrationPipelineTools(pi: Pick<ExtensionAPI, "registe
|
|
|
265
267
|
}),
|
|
266
268
|
async execute(_toolCallId, params: ConnectionParams & { chipId: string; spec: Record<string, unknown>; backendName?: string; confirmWrite?: boolean }, _signal, _onUpdate, ctx) {
|
|
267
269
|
if (!params.confirmWrite) {
|
|
268
|
-
if (!ctx.hasUI || !(await ctx.ui.confirm("Run QDash calibration pipeline?", `This starts a calibration execution on chip ${params.chipId}.`))) {
|
|
270
|
+
if (!ctx.hasUI || !(await ctx.ui.confirm("Run QDash calibration pipeline?", `This starts a calibration execution on chip ${params.chipId}. Completed tasks update calibrated parameters in QDash directly.`))) {
|
|
269
271
|
throw new Error("qdash_run_pipeline requires explicit confirmation");
|
|
270
272
|
}
|
|
271
273
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oqtopus-team/pi-qdash",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11-beta.19",
|
|
4
4
|
"description": "Pi extension for querying QDash via @oqtopus-team/qdash-client",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"access": "public"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@oqtopus-team/qdash-client": "1.9.21-beta.
|
|
30
|
+
"@oqtopus-team/qdash-client": "1.9.21-beta.203"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"@earendil-works/pi-coding-agent": "*",
|
package/skills/qdash/SKILL.md
CHANGED
|
@@ -22,7 +22,6 @@ Prefer the dedicated tool that matches the question. Use `qdash_query` only as a
|
|
|
22
22
|
- `qdash_inspect_timeseries_csv`, `qdash_compare_timeseries` — validate and compare multiple QDash or CSV timeseries
|
|
23
23
|
- `qdash_list_task_results`, `qdash_get_task_result` — find and inspect calibration results
|
|
24
24
|
- `qdash_get_task_knowledge` — interpret a task's purpose, expected result, failure modes, and check questions
|
|
25
|
-
- `qdash_list_issues` — inspect tracked issues
|
|
26
25
|
- `qdash_list_flows`, `qdash_get_flow` — list or inspect calibration flows
|
|
27
26
|
- `qdash_get_pipeline_catalog`, `qdash_plan_pipeline` — compose and check a calibration pipeline spec before running it
|
|
28
27
|
- `qdash_list_executions`, `qdash_get_execution`, `qdash_wait_execution`, `qdash_compare_executions` — inspect, await, or compare executions
|
|
@@ -57,7 +56,7 @@ Workflow:
|
|
|
57
56
|
1. `qdash_get_pipeline_catalog` — read the step types (with what each needs from an earlier step and its default task list), the one-qubit modes, and the task names by type. Use only names from this catalog.
|
|
58
57
|
2. Turn the request into steps. Start from the catalog defaults and the flow templates (`qdash_query` with `flow_templates`, then `flow_template` for one template's task list): `OneQubitCheck` and `OneQubitFineTune` carry the standard one-qubit sequences, `TwoQubitCalibration` the two-qubit chain. Use `CustomOneQubit` / `CustomTwoQubit` only where the user wants a different task list, and keep the task order the templates use.
|
|
59
58
|
3. `qdash_plan_pipeline` — validate. A result with problems is not an error: each problem carries a path into the spec (`steps[2].tasks[0]`, `targets.qids[1]`); fix it and plan again until it is valid.
|
|
60
|
-
4. Show the user the resolved steps and task counts from the plan, then `qdash_run_pipeline` with the same `chipId` and `spec`. The execution is confirmation-gated.
|
|
59
|
+
4. Show the user the resolved steps and task counts from the plan, then `qdash_run_pipeline` with the same `chipId` and `spec`. The execution is confirmation-gated. Say that completed tasks write their calibrated parameters to QDash directly; there is no candidate gate, so inside an agent calibration session use the session's actions instead.
|
|
61
60
|
5. `qdash_wait_execution` with the returned `execution_id`. It returns when the whole run has finished, not just its first step. Then summarize which targets passed and which failed, fetch the figures of failed tasks with `qdash_get_task_figures` so the user sees them, and read `qdash_get_task_knowledge` for any task whose result needs interpretation.
|
|
62
61
|
|
|
63
62
|
Rules:
|
|
@@ -160,7 +159,7 @@ When the user wants to preserve an investigated observation in QDash forum/notes
|
|
|
160
159
|
- Never print tokens, passwords, Cloudflare Access secrets, or full profile contents.
|
|
161
160
|
- Treat write/operational endpoints (agent session/action creation, candidate commits, forum create/update, flow execute, git push/pull, re-execute, admin/auth APIs) as sensitive and ask the user before any write workflow.
|
|
162
161
|
- Write-oriented QDash tools are approval-gated. In interactive pi, let the extension prompt for confirmation. In non-interactive runs, set `confirmWrite: true` only after explicit user confirmation.
|
|
163
|
-
- Use `qdash_investigate` for natural-language requests to investigate, compare, or understand a target or recent calibration; it is read-only and combines recent results with target, Forum,
|
|
162
|
+
- Use `qdash_investigate` for natural-language requests to investigate, compare, or understand a target or recent calibration; it is read-only and combines recent results with target, Forum, and recommendation context.
|
|
164
163
|
- Use `qdash_compare_calibration` when the user asks what changed between recent calibration experiments; treat differences as evidence, not automatic approval to commit/apply parameters.
|
|
165
164
|
- Use `qdash_recent_calibration_figure` when the user asks to see recent experiment images without a task ID.
|
|
166
165
|
- Use `qdash_analyze_figure_json` for read-only numeric summaries of Plotly JSON calibration figures; keep domain-specific interpretation in the matching skill.
|
|
@@ -47,7 +47,7 @@ This skill defines how pi should behave as a conservative, confirmation-gated ca
|
|
|
47
47
|
- any in-cooldown wiring changes relevant to the target
|
|
48
48
|
- failed task(s)
|
|
49
49
|
- qid/coupling_id
|
|
50
|
-
- recent executions and open
|
|
50
|
+
- recent executions and open Forum context
|
|
51
51
|
2. Read evidence:
|
|
52
52
|
- `qdash_list_task_results`
|
|
53
53
|
- `qdash_get_task_result`
|
|
@@ -144,3 +144,7 @@ Stop and request human review when:
|
|
|
144
144
|
- the same target fails after `CheckChevron` and `Configure`
|
|
145
145
|
- figures suggest TLS, frequency collision, leakage, or multi-level behavior
|
|
146
146
|
- the next action would require broad parameter sweeps or hardware-risky changes
|
|
147
|
+
- QDash refuses to run an agent action's task before it measures anything (a platform or
|
|
148
|
+
configuration error, not a calibration result). Report the error with the task result and
|
|
149
|
+
execution links; do not rerun the task through another path such as `qdash_run_pipeline`,
|
|
150
|
+
which writes parameters without the session's candidate gate
|