@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, issues, and Forum posts. Secrets are redacted from tool output.
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).
@@ -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
  }
@@ -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, openIssues, recentExecutions, failedTaskResults, provenanceStats] = await Promise.allSettled([
573
+ const [chips, openThreads, recentExecutions, failedTaskResults, provenanceStats] = await Promise.allSettled([
582
574
  client.listChips(),
583
- client.api.listIssues({ is_closed: false, limit }),
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 issuesPayload = value(openIssues);
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
- openIssues: {
596
- count: payloadTotal(issuesPayload),
597
- shown: arrayFromPayload(issuesPayload).length,
598
- items: compactItems(issuesPayload, ["issue_id", "id", "title", "task_id", "is_closed", "severity", "created_at"], limit),
599
- raw: issuesPayload,
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 issueCount = dashboard.openIssues.count > 0 ? warning(String(dashboard.openIssues.count)) : success(String(dashboard.openIssues.count));
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 issues")} ${issueCount} ${muted("executions")} ${accent(`${dashboard.recentExecutions.shown}/${dashboard.recentExecutions.count}`)} ${muted("failed tasks")} ${failedCount}`,
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 issues"),
634
- ...(dashboard.openIssues.items.length > 0 ? dashboard.openIssues.items.map((item, index) => ` ${warning(formatItem(item, `issue-${index + 1}`))}`) : [` ${dim("none")}`]),
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 issueColor = dashboard.openIssues.count > 0 ? "warning" : "success";
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 issues")} ${theme.fg(issueColor, String(dashboard.openIssues.count))}`,
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 issues"),
662
- ...(dashboard.openIssues.items.length > 0
663
- ? dashboard.openIssues.items.map((item, index) => ` ${theme.fg("warning", formatItem(item, `issue-${index + 1}`))}`)
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, issues, forumPosts] = await Promise.all([
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("issues")} ${report.issues.length ? warn(String(report.issues.length)) : "0"} ${muted("forum")} ${report.forumPosts.length}`,
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 passed = gates[0].passed && gates[1].passed;
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" : issues.length > 0 || !gates[2].passed || comparisonGate?.passed === false ? "needs_review" : "passed";
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 listed issues/figures before treating this result as authoritative.";
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, issues, failed, metrics] = await Promise.allSettled([
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, qdash_list_issues, qdash_list_flows, and qdash_list_executions when they match the task.",
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: "Link/execute a QDash agent action from a source execution. This is a write operation and requires confirmation.",
2098
- promptSnippet: "Execute or link a QDash agent action after explicit user confirmation",
2099
- promptGuidelines: ["Use qdash_execute_agent_action only after the user confirms the exact source execution and action."],
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: string; updateParams?: boolean; reconfigure?: boolean }, _signal, _onUpdate, ctx) {
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?", `Execute action '${params.actionId}' from execution '${params.sourceExecutionId}'?`))) {
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, issues, and figures. Read-only; does not commit or apply candidates.",
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, issues, forum context, and a safe next-action recommendation.",
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 issues, recent executions, failed task results, and provenance stats",
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 forum posts, notes/issues, and failed tasks into target-level insights with links.",
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, forum posts, notes, issues, or anomalous targets."],
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, issues, forum context, and safe recommendations.",
2911
- promptSnippet: "Investigate a QDash target or recent calibration by correlating results, figures, issues, and Forum context",
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 issues and failed task results for the current QDash context.",
2989
- promptSnippet: "Summarize QDash open issues and failed task results for triage",
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
- openIssues: dashboard.openIssues,
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.openIssues.count > 0 ? "Review open issues and correlate with recent executions." : undefined,
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 issues ${triage.openIssues.count > 0 ? ansi("33", String(triage.openIssues.count)) : success(String(triage.openIssues.count))}`,
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?: { openIssues?: { count?: number }; failedTaskResults?: { count?: number }; suggestedFocus?: string[] } } | undefined)?.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 issues")} ${theme.fg((data.openIssues?.count ?? 0) > 0 ? "warning" : "success", String(data.openIssues?.count ?? 0))}`,
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.openIssues.count} open issues, ${dashboard.failedTaskResults.count} failed tasks`, "info");
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.10",
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.171"
30
+ "@oqtopus-team/qdash-client": "1.9.21-beta.203"
31
31
  },
32
32
  "peerDependencies": {
33
33
  "@earendil-works/pi-coding-agent": "*",
@@ -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, issue, and recommendation context.
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 forum/issue context
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