@oqtopus-team/pi-qdash 0.1.5 → 0.1.8

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, 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, issues, 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
  - **Figures**: fetch calibration PNG/JSON figures and render them in the interactive TUI.
@@ -43,7 +43,9 @@ Or try it without installing:
43
43
  pi -e npm:@oqtopus-team/pi-qdash
44
44
  ```
45
45
 
46
- Requires pi v0.74.0 or later.
46
+ Requires Node.js 20 or later and pi v0.74.0 or later. The extension tracks
47
+ `@oqtopus-team/qdash-client` 1.9.x and uses its authenticated high-level and
48
+ generated API clients.
47
49
 
48
50
  ## Quick Start
49
51
 
@@ -4,7 +4,13 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
4
4
  import { homedir } from "node:os";
5
5
 
6
6
  import type { ExtensionAPI, Theme } from "@earendil-works/pi-coding-agent";
7
- import { QDashClient, defaultConfigPath, type DownloadedFile, type TaskResultFigureOptions } from "@oqtopus-team/qdash-client";
7
+ import {
8
+ QDashClient,
9
+ defaultConfigPath,
10
+ type CooldownResponse,
11
+ type DownloadedFile,
12
+ type TaskResultFigureOptions,
13
+ } from "@oqtopus-team/qdash-client";
8
14
  import { Text, truncateToWidth } from "@earendil-works/pi-tui";
9
15
  import { Type } from "typebox";
10
16
 
@@ -19,6 +25,7 @@ import { toTextToolResult, toToolResult } from "./lib/results.js";
19
25
  import { ansi, boxLinesToWidth, boxed, formatNumber, textComponent } from "./lib/render.js";
20
26
  import { plotSeriesLines, timeseriesPlotComponent, timeseriesPoints } from "./lib/timeseries-plot.js";
21
27
  import { installQDashWriteGate } from "./lib/write-gate.js";
28
+ import { registerTaskKnowledgeTool } from "./tools/task-knowledge.js";
22
29
  import { registerTimeseriesComparisonTool } from "./tools/timeseries.js";
23
30
  import { analyzeWiringMarkdown, wiringInsightLines, type WiringInsights } from "./lib/wiring-analysis.js";
24
31
 
@@ -397,6 +404,9 @@ async function defaultChipId(client: QDashClient, chipId?: string): Promise<stri
397
404
  return chipId ?? currentContext.chipId ?? client.getDefaultChipId();
398
405
  }
399
406
 
407
+ // Keep arbitrary GET access isolated for qdash_raw_get and legacy filters that are
408
+ // not represented in the generated OpenAPI surface. Normal operations should use
409
+ // QDashClient's high-level methods or client.api so upgrades remain type-checked.
400
410
  async function rawGet<T>(client: QDashClient, path: string, query?: Record<string, unknown>): Promise<T> {
401
411
  return (client as unknown as { get<T>(path: string, query?: Record<string, unknown>): Promise<T> }).get(path, cleanQuery(query));
402
412
  }
@@ -472,19 +482,6 @@ function requireValue(value: string | undefined, name: string): string {
472
482
  return value;
473
483
  }
474
484
 
475
- type CooldownRecord = {
476
- cooldown_id: string;
477
- cryo_id: string;
478
- description?: string;
479
- started_at: string;
480
- ended_at?: string | null;
481
- chip_ids?: string[];
482
- wiring_info?: string;
483
- wiring_blocks?: Record<string, unknown>[];
484
- };
485
-
486
- type CooldownListResponse = { cooldowns?: CooldownRecord[] };
487
-
488
485
  type CooldownWiringParams = {
489
486
  profile?: string;
490
487
  configPath?: string;
@@ -502,7 +499,7 @@ type WiringInsightParams = CooldownWiringParams & {
502
499
  color?: boolean;
503
500
  };
504
501
 
505
- function newestCooldown(cooldowns: CooldownRecord[]): CooldownRecord | undefined {
502
+ function newestCooldown(cooldowns: CooldownResponse[]): CooldownResponse | undefined {
506
503
  return [...cooldowns].sort((a, b) => {
507
504
  const activeDifference = Number(a.ended_at != null) - Number(b.ended_at != null);
508
505
  if (activeDifference !== 0) return activeDifference;
@@ -514,15 +511,15 @@ async function resolveCooldownWiring(params: CooldownWiringParams) {
514
511
  params = applyQDashContext(params);
515
512
  const client = await makeClient(params);
516
513
  let selection: string;
517
- let cooldown: CooldownRecord;
514
+ let cooldown: CooldownResponse;
518
515
  let resolvedChipId = params.chipId ?? currentContext.chipId;
519
516
 
520
517
  if (params.cooldownId) {
521
- cooldown = await rawGet<CooldownRecord>(client, `/cooldowns/${pathPart(params.cooldownId)}`);
518
+ cooldown = await client.api.getCooldown(params.cooldownId);
522
519
  selection = "explicit cooldownId";
523
520
  } else {
524
521
  resolvedChipId = params.chipId ?? (params.cryoId ? undefined : await defaultChipId(client));
525
- const response = await rawGet<CooldownListResponse>(client, "/cooldowns", {
522
+ const response = await client.api.listCooldowns({
526
523
  cryo_id: params.cryoId,
527
524
  chip_id: resolvedChipId,
528
525
  });
@@ -531,7 +528,7 @@ async function resolveCooldownWiring(params: CooldownWiringParams) {
531
528
  const scope = params.cryoId ? `cryo ${params.cryoId}` : `chip ${resolvedChipId}`;
532
529
  throw new Error(`No cooldown containing wiring information was found for ${scope}`);
533
530
  }
534
- cooldown = await rawGet<CooldownRecord>(client, `/cooldowns/${pathPart(selected.cooldown_id)}`);
531
+ cooldown = await client.api.getCooldown(selected.cooldown_id);
535
532
  selection = params.cryoId ? "active/newest cooldown for cryo" : "active/newest cooldown for chip";
536
533
  }
537
534
 
@@ -539,14 +536,14 @@ async function resolveCooldownWiring(params: CooldownWiringParams) {
539
536
  const wiringInfo = typeof cooldown.wiring_info === "string" ? cooldown.wiring_info.trim() : "";
540
537
  const includeBlocks = params.includeBlocks === true || !wiringInfo;
541
538
  const history = params.includeHistory
542
- ? await rawGet(client, `/cooldowns/${pathPart(cooldown.cooldown_id)}/wiring-events`, {
539
+ ? await client.api.listCooldownWiringEvents(cooldown.cooldown_id, {
543
540
  limit: params.historyLimit ?? 20,
544
541
  skip: 0,
545
542
  })
546
543
  : undefined;
547
544
  let cryostat: unknown;
548
545
  try {
549
- cryostat = await rawGet(client, `/cryostats/${pathPart(cooldown.cryo_id)}`);
546
+ cryostat = await client.api.getCryostat(cooldown.cryo_id);
550
547
  } catch {
551
548
  // Wiring remains useful on servers where cryostat detail is not accessible.
552
549
  }
@@ -594,9 +591,9 @@ async function buildDashboard(params: { profile?: string; configPath?: string; u
594
591
  const chipId = await defaultChipId(client, params.chipId);
595
592
  const [chips, openIssues, recentExecutions, failedTaskResults, provenanceStats] = await Promise.allSettled([
596
593
  client.listChips(),
597
- rawGet(client, "/issues", { is_closed: false, limit }),
598
- rawGet(client, "/executions", { chip_id: chipId, limit }),
599
- rawGet(client, "/task-results", { chip_id: chipId, status: "failed", limit }),
594
+ client.api.listIssues({ is_closed: false, limit }),
595
+ client.api.listExecutions({ chip_id: chipId, limit }),
596
+ client.api.listTaskResults({ chip_id: chipId, status: "failed", limit }),
600
597
  client.getProvenanceStats(),
601
598
  ]);
602
599
  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) };
@@ -738,7 +735,7 @@ async function buildRecentCalibrationSummary(params: { profile?: string; configP
738
735
  const chipId = await defaultChipId(client, params.chipId);
739
736
  const end = new Date();
740
737
  const start = new Date(end.getTime() - (params.withinHours ?? 24) * 3600_000);
741
- const payload = await rawGet(client, "/task-results", { chip_id: chipId, start_from: start.toISOString(), start_to: end.toISOString(), limit: params.limit ?? 30 });
738
+ const payload = await client.api.listTaskResults({ chip_id: chipId, start_from: start.toISOString(), start_to: end.toISOString(), limit: params.limit ?? 30 });
742
739
  const items = arrayFromPayload(payload).filter((item): item is Record<string, unknown> => Boolean(item) && typeof item === "object");
743
740
  const byTarget = new Map<string, Record<string, unknown>[]>();
744
741
  for (const item of items) {
@@ -939,7 +936,7 @@ async function buildTargetOperationsReport(params: { profile?: string; configPat
939
936
  const [latestTarget, results, issues, forumPosts] = await Promise.all([
940
937
  kind === "qubit" ? client.getChipQubit(chipId, id) : client.getChipCoupling(chipId, id),
941
938
  client.listTaskResults({ chipId, qid, couplingId, startAt: start.toISOString(), endAt: end.toISOString(), limit }),
942
- rawGet(client, "/issues", { is_closed: false, limit: 100 }),
939
+ client.api.listIssues({ is_closed: false, limit: 100 }),
943
940
  client.listForumPosts({ chipId, status: "open", targetType: kind, targetId: id, limit }),
944
941
  ]);
945
942
  const recentResults = arrayFromPayload(results).filter((item): item is Record<string, unknown> => Boolean(item) && typeof item === "object");
@@ -1123,7 +1120,7 @@ async function buildCalibrationValidation(params: { profile?: string; configPath
1123
1120
  const task = await client.getTaskResult(params.taskId) as unknown as Record<string, unknown>;
1124
1121
  const status = firstString(task, ["status"])?.toLowerCase() ?? "unknown";
1125
1122
  const figurePaths = taskFigurePaths(task, 20);
1126
- const issuesPayload = await rawGet(client, `/task-results/${pathPart(params.taskId)}/issues`).catch(() => []);
1123
+ const issuesPayload = await client.api.getTaskResultIssues(params.taskId).catch(() => []);
1127
1124
  const issues = arrayFromPayload(issuesPayload).filter((item): item is Record<string, unknown> => Boolean(item) && typeof item === "object");
1128
1125
  const afterExecutionId = firstString(task, ["execution_id", "executionId"]);
1129
1126
  const comparison = params.beforeExecutionId && afterExecutionId
@@ -1256,10 +1253,10 @@ async function buildDashboardInsights(params: { profile?: string; configPath?: s
1256
1253
  const start = new Date(end.getTime() - (params.withinHours ?? 168) * 3600_000);
1257
1254
  const [forums, issues, failed, metrics, aiInsights] = await Promise.allSettled([
1258
1255
  client.listForumPosts({ chipId, status: "open", limit }),
1259
- rawGet(client, "/issues", { is_closed: false, limit }),
1260
- rawGet(client, "/task-results", { chip_id: chipId, status: "failed", start_from: start.toISOString(), start_to: end.toISOString(), limit }),
1256
+ client.api.listIssues({ is_closed: false, limit }),
1257
+ client.api.listTaskResults({ chip_id: chipId, status: "failed", start_from: start.toISOString(), start_to: end.toISOString(), limit }),
1261
1258
  client.getChipMetrics(chipId),
1262
- rawGet(client, `/chips/${pathPart(chipId)}/ai-insights`, { latest_only: true, start_at: start.toISOString(), end_at: end.toISOString() }),
1259
+ client.api.getDashboardAiInsights(chipId, { latest_only: true, start_at: start.toISOString(), end_at: end.toISOString() }),
1263
1260
  ]);
1264
1261
  const value = <T>(result: PromiseSettledResult<T>): T | undefined => result.status === "fulfilled" ? result.value : undefined;
1265
1262
  const insightMap = new Map<string, DashboardInsight>();
@@ -1485,19 +1482,19 @@ async function executeQuery(params: QDashQueryParams) {
1485
1482
  case "chip_qubit": return client.getChipQubit(await defaultChipId(client, params.chipId), requireValue(params.qid, "qid"));
1486
1483
  case "chip_couplings": return client.listChipCouplings(await defaultChipId(client, params.chipId), { limit: params.limit, offset: params.offset });
1487
1484
  case "chip_coupling": return client.getChipCoupling(await defaultChipId(client, params.chipId), requireValue(params.couplingId, "couplingId"));
1488
- case "cryostats": return rawGet(client, "/cryostats");
1489
- case "cryostat": return rawGet(client, `/cryostats/${pathPart(requireValue(params.cryoId, "cryoId"))}`);
1490
- case "cooldowns": return rawGet(client, "/cooldowns", { cryo_id: params.cryoId, chip_id: params.chipId });
1491
- case "cooldown": return rawGet(client, `/cooldowns/${pathPart(requireValue(params.cooldownId, "cooldownId"))}`);
1492
- case "cooldown_wiring_events": return rawGet(client, `/cooldowns/${pathPart(requireValue(params.cooldownId, "cooldownId"))}/wiring-events`, { limit: params.limit, skip: params.skip });
1485
+ case "cryostats": return client.api.listCryostats();
1486
+ case "cryostat": return client.api.getCryostat(requireValue(params.cryoId, "cryoId"));
1487
+ case "cooldowns": return client.api.listCooldowns({ cryo_id: params.cryoId, chip_id: params.chipId });
1488
+ case "cooldown": return client.api.getCooldown(requireValue(params.cooldownId, "cooldownId"));
1489
+ case "cooldown_wiring_events": return client.api.listCooldownWiringEvents(requireValue(params.cooldownId, "cooldownId"), { limit: params.limit, skip: params.skip });
1493
1490
  case "timeseries": return client.getTaskResultsTimeseries({ chipId: await defaultChipId(client, params.chipId), parameter: requireValue(params.parameter, "parameter"), tag: params.tag, qid: params.qid, startAt: requireValue(params.startAt, "startAt"), endAt: requireValue(params.endAt, "endAt") });
1494
1491
  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 });
1495
1492
  case "task_result": return client.getTaskResult(requireValue(params.taskId, "taskId"));
1496
- case "task_note": return rawGet(client, `/task-results/${pathPart(requireValue(params.taskId, "taskId"))}/note`);
1497
- case "task_result_issues": return rawGet(client, `/task-results/${pathPart(requireValue(params.taskId, "taskId"))}/issues`);
1498
- case "qubit_latest": return rawGet(client, "/task-results/qubits/latest", { chip_id: await defaultChipId(client, params.chipId), task: requireValue(params.task ?? params.taskName, "task") });
1493
+ case "task_note": return client.api.getTaskNote(requireValue(params.taskId, "taskId"));
1494
+ case "task_result_issues": return client.api.getTaskResultIssues(requireValue(params.taskId, "taskId"));
1495
+ case "qubit_latest": return client.api.getLatestQubitTaskResults({ chip_id: await defaultChipId(client, params.chipId), task: requireValue(params.task ?? params.taskName, "task") });
1499
1496
  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") });
1500
- case "coupling_latest": return rawGet(client, "/task-results/couplings/latest", { chip_id: await defaultChipId(client, params.chipId), task: requireValue(params.task ?? params.taskName, "task") });
1497
+ case "coupling_latest": return client.api.getLatestCouplingTaskResults({ chip_id: await defaultChipId(client, params.chipId), task: requireValue(params.task ?? params.taskName, "task") });
1501
1498
  case "coupling_history": return rawGet(client, `/task-results/couplings/${pathPart(requireValue(params.couplingId, "couplingId"))}/history`, { chip_id: await defaultChipId(client, params.chipId), task: requireValue(params.task ?? params.taskName, "task"), date: requireValue(params.date, "date") });
1502
1499
  case "tasks": return client.listTasks(params.backend);
1503
1500
  case "task_knowledge": return params.taskName ? client.getTaskKnowledge(params.taskName) : client.listTaskKnowledge();
@@ -1507,23 +1504,26 @@ async function executeQuery(params: QDashQueryParams) {
1507
1504
  case "files_tree": return client.getFilesTree();
1508
1505
  case "file_content": return client.getFileContent(requireValue(params.path, "path"));
1509
1506
  case "git_status": return client.getGitStatus();
1510
- case "issues": return rawGet(client, "/issues", { task_id: params.taskId, is_closed: params.isClosed, limit: params.limit, skip: params.skip });
1511
- case "issue_knowledge": return rawGet(client, "/issue-knowledge", { status: params.status, task_name: params.taskName, limit: params.limit, skip: params.skip });
1507
+ case "issues": return client.api.listIssues({ task_id: params.taskId, is_closed: params.isClosed, limit: params.limit, skip: params.skip });
1508
+ case "issue_knowledge": return client.api.listIssueKnowledge({ status: params.status, task_name: params.taskName, limit: params.limit, skip: params.skip });
1512
1509
  case "flows": return client.listFlows();
1513
1510
  case "flow": return client.getFlow(requireValue(params.flowName, "flowName"));
1514
1511
  case "flow_templates": return client.listFlowTemplates();
1515
1512
  case "flow_template": return client.getFlowTemplate(requireValue(params.templateId, "templateId"));
1516
- case "flow_helper_files": return rawGet(client, "/flows/helpers");
1517
- case "flow_helper_file": return rawGet(client, `/flows/helpers/${pathPart(requireValue(params.filename, "filename"))}`);
1513
+ case "flow_helper_files": return client.api.listFlowHelperFiles();
1514
+ case "flow_helper_file": return client.api.getFlowHelperFile(requireValue(params.filename, "filename"));
1518
1515
  case "executions": return rawGet(client, "/executions", { chip_id: params.chipId, flow_name: params.flowName, status: params.status, skip: params.skip, limit: params.limit });
1519
1516
  case "execution": return client.getExecution(requireValue(params.executionId, "executionId"));
1520
- case "ai_reviews": return rawGet(client, "/task-results/ai-review", { chip_id: params.chipId, task_name: params.taskName, status: params.status, decision: params.decision, latest_only: params.latestOnly, skip: params.skip, limit: params.limit });
1521
- case "ai_review_runs": return rawGet(client, "/task-results/ai-review/runs", { chip_id: params.chipId, task_name: params.taskName, skip: params.skip, limit: params.limit });
1522
- case "ai_review_run": return rawGet(client, `/task-results/ai-review/runs/${pathPart(requireValue(params.reviewRunId, "reviewRunId"))}`);
1517
+ case "ai_reviews": return client.api.listTaskResultAiReviews({ chip_id: params.chipId, task_name: params.taskName, status: params.status, decision: params.decision, latest_only: params.latestOnly, skip: params.skip, limit: params.limit });
1518
+ case "ai_review_runs": return client.api.listTaskResultAiReviewRuns({ chip_id: params.chipId, task_name: params.taskName, skip: params.skip, limit: params.limit });
1519
+ case "ai_review_run": return client.api.getTaskResultAiReviewRun(requireValue(params.reviewRunId, "reviewRunId"));
1523
1520
  case "forum_posts": return client.listForumPosts({ status: params.status, chipId: params.chipId, limit: params.limit, skip: params.skip });
1524
1521
  case "provenance_stats": return client.getProvenanceStats();
1525
- case "provenance_history": return rawGet(client, "/provenance/history", { parameter_name: params.parameterName ?? params.parameter, qid: params.qid, limit: params.limit });
1526
- case "provenance_changes": return rawGet(client, "/provenance/changes", { parameter_names: params.parameterName ?? params.parameter, within_hours: params.withinHours, limit: params.limit });
1522
+ case "provenance_history": return client.api.getParameterHistory({ parameter_name: requireValue(params.parameterName ?? params.parameter, "parameterName"), qid: requireValue(params.qid, "qid"), limit: params.limit });
1523
+ case "provenance_changes": {
1524
+ const parameterName = params.parameterName ?? params.parameter;
1525
+ return client.getRecentChanges({ parameterNames: parameterName ? [parameterName] : undefined, withinHours: params.withinHours, limit: params.limit });
1526
+ }
1527
1527
  case "provenance_lineage": return client.getProvenanceLineage(requireValue(params.entityId, "entityId"));
1528
1528
  case "provenance_impact": return client.getProvenanceImpact(requireValue(params.entityId, "entityId"));
1529
1529
  }
@@ -1531,6 +1531,7 @@ async function executeQuery(params: QDashQueryParams) {
1531
1531
 
1532
1532
  export default function qdashExtension(pi: ExtensionAPI) {
1533
1533
  installQDashWriteGate(pi);
1534
+ registerTaskKnowledgeTool(pi, { makeClient });
1534
1535
 
1535
1536
  pi.registerTool({
1536
1537
  name: "qdash_config_info",
@@ -0,0 +1,144 @@
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ import type { QDashClient } from "@oqtopus-team/qdash-client";
3
+ import { Type } from "typebox";
4
+
5
+ import { toTextToolResult } from "../lib/results.js";
6
+
7
+ type TaskKnowledgeParams = {
8
+ profile?: string;
9
+ configPath?: string;
10
+ useEnv?: boolean;
11
+ taskName: string;
12
+ format?: "markdown" | "summary";
13
+ };
14
+
15
+ type TaskKnowledge = {
16
+ name: string;
17
+ summary?: string;
18
+ what_it_measures?: string;
19
+ expected_result?: Record<string, unknown>;
20
+ failure_modes?: Array<Record<string, unknown>>;
21
+ check_questions?: string[];
22
+ };
23
+
24
+ type TaskKnowledgeList = { items?: Array<{ name?: string }> } | Array<{ name?: string }>;
25
+
26
+ export type TaskKnowledgeDependencies = {
27
+ makeClient(params: TaskKnowledgeParams): Promise<QDashClient>;
28
+ };
29
+
30
+ function text(value: unknown): string | undefined {
31
+ if (typeof value !== "string") return undefined;
32
+ const trimmed = value.trim();
33
+ return trimmed || undefined;
34
+ }
35
+
36
+ function bulletLines(items: string[]): string[] {
37
+ return items.length ? items.map((item) => `- ${item}`) : ["- Not documented."];
38
+ }
39
+
40
+ export function taskKnowledgeSummary(knowledge: TaskKnowledge): string {
41
+ const expected = knowledge.expected_result ?? {};
42
+ const expectedLines = [
43
+ text(expected.description),
44
+ text(expected.good_visual) && `Good visual: ${text(expected.good_visual)}`,
45
+ text(expected.typical_range) && `Typical range: ${text(expected.typical_range)}`,
46
+ ].filter((value): value is string => Boolean(value));
47
+ const failureLines = (knowledge.failure_modes ?? []).map((mode) => {
48
+ const description = text(mode.description) ?? "Unspecified failure mode";
49
+ const cause = text(mode.cause);
50
+ const nextAction = text(mode.next_action);
51
+ return [description, cause && `Cause: ${cause}`, nextAction && `Next: ${nextAction}`].filter(Boolean).join(" — ");
52
+ });
53
+
54
+ return [
55
+ `# ${knowledge.name}`,
56
+ "",
57
+ ...(text(knowledge.summary) ? [text(knowledge.summary)!, ""] : []),
58
+ "## What it measures",
59
+ "",
60
+ text(knowledge.what_it_measures) ?? "Not documented.",
61
+ "",
62
+ "## Expected result",
63
+ "",
64
+ ...bulletLines(expectedLines),
65
+ "",
66
+ "## Failure modes",
67
+ "",
68
+ ...bulletLines(failureLines),
69
+ "",
70
+ "## Check questions",
71
+ "",
72
+ ...bulletLines((knowledge.check_questions ?? []).filter((item) => Boolean(text(item)))),
73
+ ].join("\n");
74
+ }
75
+
76
+ function knownNames(payload: TaskKnowledgeList): string[] {
77
+ const items = Array.isArray(payload) ? payload : payload.items ?? [];
78
+ return items.map((item) => text(item.name)).filter((name): name is string => Boolean(name));
79
+ }
80
+
81
+ export function similarTaskNames(taskName: string, names: string[], limit = 5): string[] {
82
+ const needle = taskName.toLowerCase();
83
+ return names
84
+ .filter((name) => {
85
+ const candidate = name.toLowerCase();
86
+ return candidate.startsWith(needle) || needle.startsWith(candidate) || candidate.includes(needle) || needle.includes(candidate);
87
+ })
88
+ .sort((a, b) => {
89
+ const aPrefix = a.toLowerCase().startsWith(needle) ? 0 : 1;
90
+ const bPrefix = b.toLowerCase().startsWith(needle) ? 0 : 1;
91
+ return aPrefix - bPrefix || a.length - b.length || a.localeCompare(b);
92
+ })
93
+ .slice(0, limit);
94
+ }
95
+
96
+ function notFound(error: unknown): boolean {
97
+ if (!error || typeof error !== "object") return false;
98
+ const value = error as { status?: unknown; statusCode?: unknown; response?: { status?: unknown }; message?: unknown };
99
+ return value.status === 404 || value.statusCode === 404 || value.response?.status === 404 || /\b404\b|not found/i.test(String(value.message ?? ""));
100
+ }
101
+
102
+ export function registerTaskKnowledgeTool(pi: Pick<ExtensionAPI, "registerTool">, deps: TaskKnowledgeDependencies): void {
103
+ pi.registerTool({
104
+ name: "qdash_get_task_knowledge",
105
+ label: "QDash Task Knowledge",
106
+ description: "Get task knowledge when asked how to interpret a result, what curve or outcome is expected, which failure modes apply, or what to try next.",
107
+ promptSnippet: "Read QDash task knowledge before interpreting calibration results",
108
+ promptGuidelines: [
109
+ "Use qdash_get_task_knowledge before interpreting task-specific physics, expected curves, failure modes, or next diagnostic checks.",
110
+ "This tool is read-only and does not execute tasks or change parameters.",
111
+ ],
112
+ parameters: Type.Object({
113
+ profile: Type.Optional(Type.String({ description: "QDash profile name. Defaults to env when QDASH_BASE_URL is set, otherwise 'default'." })),
114
+ configPath: Type.Optional(Type.String({ description: "Optional path to qdash config.ini." })),
115
+ useEnv: Type.Optional(Type.Boolean({ description: "Force QDASH_* environment variables instead of a profile." })),
116
+ taskName: Type.String({ description: "QDash task name, for example CheckRabi." }),
117
+ format: Type.Optional(Type.Union([Type.Literal("markdown"), Type.Literal("summary")], { description: "Output format. Defaults to markdown." })),
118
+ }),
119
+ async execute(_toolCallId, params: TaskKnowledgeParams) {
120
+ const client = await deps.makeClient(params);
121
+ const format = params.format ?? "markdown";
122
+ try {
123
+ const data = format === "summary"
124
+ ? await client.getTaskKnowledge(params.taskName) as unknown as TaskKnowledge
125
+ : await client.getTaskKnowledgeMarkdown(params.taskName);
126
+ const markdown = format === "summary"
127
+ ? taskKnowledgeSummary(data as TaskKnowledge)
128
+ : typeof data === "string"
129
+ ? data
130
+ : text((data as { markdown?: unknown })?.markdown) ?? JSON.stringify(data, null, 2);
131
+ return toTextToolResult(markdown, data, { taskName: params.taskName, format, found: true });
132
+ } catch (error) {
133
+ if (!notFound(error)) throw error;
134
+ const listed = await client.listTaskKnowledge() as TaskKnowledgeList;
135
+ const suggestions = similarTaskNames(params.taskName, knownNames(listed));
136
+ const markdown = [
137
+ `No task knowledge found for \`${params.taskName}\`.`,
138
+ ...(suggestions.length ? ["", "Similar known task names:", ...suggestions.map((name) => `- \`${name}\``)] : ["", "No similar known task names were found."]),
139
+ ].join("\n");
140
+ return toTextToolResult(markdown, { taskName: params.taskName, found: false, suggestions }, { taskName: params.taskName, format, found: false });
141
+ }
142
+ },
143
+ });
144
+ }
package/package.json CHANGED
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "name": "@oqtopus-team/pi-qdash",
3
- "version": "0.1.5",
3
+ "version": "0.1.8",
4
4
  "description": "Pi extension for querying QDash via @oqtopus-team/qdash-client",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
7
7
  "author": "OQTOPUS Team <oqtopus-team@googlegroups.com>",
8
+ "engines": {
9
+ "node": ">=20.0.0"
10
+ },
8
11
  "repository": {
9
12
  "type": "git",
10
13
  "url": "git+https://github.com/oqtopus-team/pi-qdash.git"
@@ -24,7 +27,7 @@
24
27
  "access": "public"
25
28
  },
26
29
  "dependencies": {
27
- "@oqtopus-team/qdash-client": "^0.1.4"
30
+ "@oqtopus-team/qdash-client": "^1.9.11"
28
31
  },
29
32
  "peerDependencies": {
30
33
  "@earendil-works/pi-coding-agent": "*",
@@ -9,55 +9,45 @@ Use the pi-qdash tools instead of scraping the UI or hand-writing auth headers.
9
9
 
10
10
  ## Preferred tools
11
11
 
12
- 1. `qdash_config_info` — check profile names and non-secret connection settings.
13
- 2. Prefer dedicated read-only tools when they match the task:
14
- - `qdash_list_chips`, `qdash_get_default_chip`
15
- - `qdash_get_chip_metrics`, `qdash_list_chip_qubits`, `qdash_list_chip_couplings`
16
- - `qdash_list_cryostats`, `qdash_list_cooldowns`
17
- - `qdash_get_cooldown_wiring`, `qdash_wiring_insights`, `qdash_list_cooldown_wiring_events`
18
- - `qdash_get_timeseries`, `qdash_plot_timeseries`, `qdash_inspect_timeseries_csv`, `qdash_compare_timeseries`
19
- - `qdash_list_task_results`, `qdash_get_task_result`
20
- - `qdash_list_issues`
21
- - `qdash_list_flows`, `qdash_get_flow`
22
- - `qdash_list_executions`, `qdash_get_execution`
23
- - `qdash_list_ai_reviews`, `qdash_get_provenance_stats`
24
- - `qdash_list_forum_posts`, `qdash_get_forum_post`, `qdash_list_forum_replies`
25
- - `qdash_get_figure`, `qdash_get_task_figures`, `qdash_recent_calibration_figure`, `qdash_analyze_figure_json`, `qdash_build_qcal_evidence`
26
- - `qdash_create_forum_evidence_reply` for confirmed evidence curation replies with visible task figures
27
- - `qdash_preview_forum_image_reply`, then `qdash_create_forum_image_reply` for confirmed uploads of locally generated analysis images
28
- 3. Use harness overview tools for status and triage:
29
- - `qdash_investigate` for natural-language requests to investigate or compare recent calibration, target history, figures, failures, issues, and Forum context
30
- - `qdash_compare_calibration` for read-only before/after comparison of repeated target calibration results
31
- - `qdash_dashboard`
32
- - `qdash_dashboard_insights`
33
- - `qdash_recent_calibration_summary`
34
- - `qdash_recommend_next_action`
35
- - `qdash_triage_overview`
36
- 4. Use agent calibration workflow tools when the user explicitly wants an agent workflow:
37
- - `qdash_create_agent_session`
38
- - `qdash_get_agent_session`
39
- - `qdash_submit_agent_action`
40
- - `qdash_get_agent_action`, `qdash_list_agent_actions`, `qdash_wait_agent_action`
41
- - `qdash_list_agent_action_candidates`
42
- - `qdash_execute_agent_action`
43
- - `qdash_commit_agent_candidate`, `qdash_commit_agent_campaign_candidates`
44
- - `qdash_get_agent_candidate_commit`, `qdash_apply_agent_candidate_commit`, `qdash_wait_agent_candidate_apply`
45
- 5. Use `qdash_query` for common read-only QDash operations not covered by a dedicated tool:
46
- - `chips`, `default_chip`, `metrics_config`, `chip_metrics`
47
- - `chip_qubits`, `chip_qubit`, `chip_couplings`, `chip_coupling`
48
- - `cryostats`, `cryostat`, `cooldowns`, `cooldown`, `cooldown_wiring_events`
49
- - `timeseries`, `task_results`, `task_result`, `task_note`, `task_result_issues`
50
- - `qubit_latest`, `qubit_history`, `coupling_latest`, `coupling_history`
51
- - `tasks`, `task_knowledge`, `task_knowledge_markdown`
52
- - `projects`, `project`, `files_tree`, `file_content`, `git_status`
53
- - `issues`, `issue_knowledge`
54
- - `flows`, `flow`, `flow_templates`, `flow_template`, `flow_helper_files`, `flow_helper_file`
55
- - `executions`, `execution`, `ai_reviews`, `ai_review_runs`, `ai_review_run`
56
- - `forum_posts`, `provenance_stats`, `provenance_history`, `provenance_changes`, `provenance_lineage`, `provenance_impact`
57
- 6. `qdash_raw_get` — read-only GET endpoints not covered by `qdash_query`.
12
+ Prefer the dedicated tool that matches the question. Use `qdash_query` only as a fallback when no dedicated tool covers the read-only operation.
13
+
14
+ 1. Dedicated read-only tools:
15
+ - `qdash_list_chips`, `qdash_get_default_chip` — list chips or resolve the active chip
16
+ - `qdash_get_chip_metrics`, `qdash_list_chip_qubits`, `qdash_list_chip_couplings` — inspect chip metrics and topology targets
17
+ - `qdash_list_cryostats`, `qdash_list_cooldowns` — establish cryostat and cooldown context
18
+ - `qdash_get_cooldown_wiring`, `qdash_wiring_insights`, `qdash_list_cooldown_wiring_events` — inspect current wiring, attenuation, and changes
19
+ - `qdash_get_timeseries`, `qdash_plot_timeseries` — inspect one target metric over time
20
+ - `qdash_inspect_timeseries_csv`, `qdash_compare_timeseries` — validate and compare multiple QDash or CSV timeseries
21
+ - `qdash_list_task_results`, `qdash_get_task_result` — find and inspect calibration results
22
+ - `qdash_get_task_knowledge` — interpret a task's purpose, expected result, failure modes, and check questions
23
+ - `qdash_list_issues` — inspect tracked issues
24
+ - `qdash_list_flows`, `qdash_get_flow` — list or inspect calibration flows
25
+ - `qdash_list_executions`, `qdash_get_execution`, `qdash_wait_execution`, `qdash_compare_executions` — inspect, await, or compare executions
26
+ - `qdash_list_ai_reviews`, `qdash_get_provenance_stats` — inspect AI reviews or provenance status
27
+ - `qdash_list_forum_posts`, `qdash_get_forum_post`, `qdash_list_forum_replies` — inspect Forum context
28
+ - `qdash_preview_forum_evidence_reply`, `qdash_preview_forum_image_reply` — preview evidence or image replies without writing
29
+ - `qdash_get_figure`, `qdash_get_task_figures`, `qdash_recent_calibration_figure`, `qdash_analyze_figure_json`, `qdash_build_qcal_evidence` — inspect or package figure evidence
30
+ 2. Read-only overview and planning tools:
31
+ - `qdash_dashboard`, `qdash_dashboard_insights`, `qdash_triage_overview` — summarize status, insights, and investigation priorities
32
+ - `qdash_investigate`, `qdash_target_report`, `qdash_compare_calibration` — investigate target history and compare calibrations
33
+ - `qdash_recent_calibration_summary`, `qdash_recommend_next_action` — summarize outcomes and recommend a safe next action
34
+ - `qdash_degradation_report`, `qdash_plan_calibration`, `qdash_validate_calibration` — inspect drift, plan work, and validate results
35
+ 3. Write-oriented workflow tools, only after explicit confirmation:
36
+ - `qdash_create_agent_session`, `qdash_submit_agent_action`, `qdash_execute_agent_action` — create or execute a scoped agent workflow
37
+ - `qdash_commit_agent_candidate`, `qdash_commit_agent_campaign_candidates` — commit reviewed candidate parameters
38
+ - `qdash_apply_agent_candidate_commit` — apply a reviewed candidate commit
39
+ - `qdash_create_forum_post`, `qdash_update_forum_post` — create or update Forum content
40
+ - `qdash_create_forum_evidence_reply`, `qdash_create_forum_image_reply` — publish previously reviewed evidence replies
41
+ 4. Agent workflow state tools:
42
+ - `qdash_get_agent_session`, `qdash_get_agent_action`, `qdash_list_agent_actions`, `qdash_wait_agent_action` — inspect agent workflow state
43
+ - `qdash_list_agent_action_candidates`, `qdash_get_agent_candidate_commit`, `qdash_wait_agent_candidate_apply` — inspect candidates and apply status
44
+ 5. `qdash_query` — fallback for read-only operations without a dedicated tool.
45
+ 6. `qdash_raw_get` — last-resort read-only GET for an uncovered endpoint.
58
46
 
59
47
  ## Session context
60
48
 
49
+ When running in pi CLI, use `qdash_config_info` if the profile or non-secret connection settings are unclear.
50
+
61
51
  Use these commands to make pi behave like a QDash-specific harness with persistent session-local context:
62
52
 
63
53
  ```text
@@ -76,6 +66,20 @@ Use these commands to make pi behave like a QDash-specific harness with persiste
76
66
 
77
67
  Prefer the current context when the user has already selected a profile/chip/session. Tools use that context when parameters are omitted.
78
68
 
69
+ ## Cooldown-aware investigations
70
+
71
+ Treat cooldown identity as mandatory investigation context whenever comparing calibration results, coherence metrics, drift, failures, or Forum evidence across time:
72
+
73
+ 1. Call `qdash_list_cooldowns` for the chip early in the investigation and record the relevant `cooldown_id`, `started_at`, and `ended_at` boundaries.
74
+ 2. Assign every result or Forum observation used in a comparison to a cooldown. Prefer an explicit `cooldown_id`; when it is absent, infer membership only from the chip association and timestamp boundaries, and label it as inferred.
75
+ 3. Do not describe values from different cooldowns as one continuous degradation, recovery, or before/after trend. Partition the evidence by cooldown first. Cross-cooldown comparison is allowed only when it is explicitly identified as such.
76
+ 4. State plainly whether the compared evidence comes from the same cooldown, different cooldowns, or an unresolved cooldown context. Include the cooldown IDs in the report and in Forum evidence replies when relevant.
77
+ 5. Same cooldown does not guarantee identical measurement conditions. If cabling, attenuation, instruments, or wiring may have changed, inspect `qdash_get_cooldown_wiring` with history or `qdash_list_cooldown_wiring_events`, then distinguish a cooldown boundary from an in-cooldown wiring change.
78
+ 6. Do not treat a temporary disconnect/reconnect as a new cooldown unless QDash records a new cooldown. Describe it as an in-cooldown wiring event and report whether the wiring was nominally restored.
79
+ 7. Prefer same-cooldown, temporally adjacent measurements for physical interpretation. Older-cooldown evidence may provide context but must not be used to explain a current-cooldown anomaly without an explicit caveat.
80
+
81
+ Before publishing investigated evidence, verify that every date-based claim respects these rules.
82
+
79
83
  ## Generic timeseries comparison
80
84
 
81
85
  Use `qdash_compare_timeseries` when the investigation needs to align multiple
@@ -116,8 +120,8 @@ If `QDASH_BASE_URL` is set and no profile is specified, the tools default to env
116
120
 
117
121
  When the user wants to preserve an investigated observation in QDash forum/notes:
118
122
 
119
- 1. Inspect the task result, task figure, timeseries/history, and related forum context first.
120
- 2. Summarize the observation as evidence, not as an automatic calibration decision.
123
+ 1. Inspect the task result, task figure, timeseries/history, related Forum context, and cooldown membership first.
124
+ 2. Summarize the observation as evidence, not as an automatic calibration decision. State whether compared observations are from the same cooldown, and separate in-cooldown wiring changes from cooldown boundaries.
121
125
  3. Prefer replying to an existing target/coupling forum thread when one exists.
122
126
  4. Use `qdash_create_forum_evidence_reply` for task-result evidence so figures are embedded as QDash UI image blocks (`/api/executions/figure?path=...`) and visible in the forum.
123
127
  5. For locally generated analysis images, call `qdash_preview_forum_image_reply` first, show the exact paths and reply text, then use `qdash_create_forum_image_reply` only after explicit confirmation. Supported images are PNG, JPEG, GIF, and WebP up to 5 MB each.
@@ -9,7 +9,7 @@ metadata:
9
9
  maturity: experimental
10
10
  safety_level: operational-write-gated
11
11
  default_mode: one-target-one-action
12
- source_of_truth: pi-qdash skills until QDash task-knowledge operation is finalized
12
+ source_of_truth: procedures in pi-qdash skills; task physics and interpretation in qdash-task-knowledge
13
13
  preferred_qubit_recovery_recipe: CheckRabi -> CheckChevron -> Configure -> CheckRabi
14
14
  requires_confirmation_for:
15
15
  - agent session creation
@@ -26,28 +26,32 @@ metadata:
26
26
 
27
27
  # QDash Calibration Agent
28
28
 
29
- This skill defines how pi should behave as a conservative, confirmation-gated calibration execution agent. It should orchestrate safe operational steps after diagnosis. Long-lived physics/task knowledge may later move to QDash task-knowledge; detailed read-only diagnosis should live in dedicated diagnosis skills.
29
+ This skill defines how pi should behave as a conservative, confirmation-gated calibration execution agent. It orchestrates safe operational steps after diagnosis. Task physics, expected results, and failure modes come from QDash task knowledge; this skill owns procedure and safety gates.
30
30
 
31
31
  ## Core principles
32
32
 
33
33
  - Work one target at a time: one qubit or one coupling per action/session when possible.
34
34
  - Diagnose before changing parameters.
35
- - Prefer read-only inspection first: dashboard, failed task results, history, figures, forum posts, and task knowledge when available.
35
+ - Establish cooldown identity before interpreting history: partition evidence by `cooldown_id`, never present different cooldowns as one continuous trend, and distinguish cooldown boundaries from in-cooldown wiring changes.
36
+ - Prefer read-only inspection first: dashboard, cooldowns and wiring events, failed task results, history, figures, forum posts, and task knowledge when available.
36
37
  - Use narrow agent session policies with the smallest allowed task/action/parameter scope.
37
38
  - Do not commit or apply parameter candidates until a validation task succeeds and the user explicitly confirms.
38
39
  - Avoid blind parameter sweeps. If two conservative probes fail, step back to a diagnostic task.
39
- - Record useful lessons as task-knowledge cases or as updates to this skill.
40
+ - Record task-specific lessons as task-knowledge cases; update this skill only for reusable procedure or safety guidance.
40
41
 
41
42
  ## Standard workflow
42
43
 
43
44
  1. Establish context:
44
45
  - profile and chip
46
+ - active/relevant `cooldown_id` and its time boundaries via `qdash_list_cooldowns`
47
+ - any in-cooldown wiring changes relevant to the target
45
48
  - failed task(s)
46
49
  - qid/coupling_id
47
50
  - recent executions and open forum/issue context
48
51
  2. Read evidence:
49
52
  - `qdash_list_task_results`
50
53
  - `qdash_get_task_result`
54
+ - `qdash_get_task_knowledge` for every task whose result will be interpreted
51
55
  - `qdash_get_task_figures` when figures exist
52
56
  - forum posts for the target when relevant
53
57
  3. Make a one-step plan.
@@ -61,7 +65,7 @@ This skill defines how pi should behave as a conservative, confirmation-gated ca
61
65
 
62
66
  ### CheckRabi returns non-finite frequency, NaN, or low R²
63
67
 
64
- Do not repeatedly perturb `control_amplitude`, `readout_amplitude`, or `shots` without diagnosis.
68
+ Read `CheckRabi` and `CheckChevron` with `qdash_get_task_knowledge` before interpreting the failure or proposing recovery. Do not repeatedly perturb parameters without diagnosis.
65
69
 
66
70
  Canonical recovery sequence:
67
71
 
@@ -78,26 +82,9 @@ Recommended sequence:
78
82
  5. If successful, inspect candidates and only then consider committing/applying parameters with explicit user confirmation.
79
83
  6. If still failing after `Configure`, request human review and inspect raw/figure data.
80
84
 
81
- Known successful pattern:
82
-
83
- - Q33 on mackerel / `144Qv1`, 2026-07-14:
84
- - repeated `CheckRabi` failures with `non-finite frequency: nan`
85
- - `CheckChevron` succeeded and estimated:
86
- - `qubit_frequency = 4.186021437949582 GHz`
87
- - `control_amplitude = 0.196626929963469 a.u.`
88
- - `Configure` completed
89
- - `CheckRabi` then completed with the CheckChevron estimates:
90
- - `rabi_frequency = 12.664970037417886 MHz`
91
- - `control_amplitude = 0.19406572753680693 a.u.`
92
-
93
85
  ### CheckChevron interpretation and suggestions
94
86
 
95
- If `CheckChevron` completes:
96
-
97
- - Clear vertex/fringes: use estimated `qubit_frequency` and `control_amplitude`, then run `Configure -> CheckRabi` for validation.
98
- - Faint fringes: suspect insufficient drive, readout fidelity, or initialization/readout contrast; suggest readout diagnostics before parameter commits.
99
- - No visible chevron: inspect readout path first (`CheckReadoutFrequency`, `ReadoutClassification`) or request human review.
100
- - Asymmetric/double chevron: suspect TLS, higher-level transitions, AC Stark shift, or frequency collision; avoid committing simple Rabi outputs without review.
87
+ Read `CheckChevron` with `qdash_get_task_knowledge` before judging its figure, estimates, expected curve, or failure mode.
101
88
 
102
89
  If `CheckChevron` fails:
103
90
 
@@ -106,36 +93,11 @@ If `CheckChevron` fails:
106
93
  3. Classify the failure message and input/output parameters.
107
94
  4. Suggest the next diagnostic, but require user confirmation before another operational action.
108
95
 
109
- Common failed-Chevron suggestions:
110
-
111
- - `Qubit frequency too low/high`, boundary hit, or fitted frequency outside normal operating range:
112
- - Treat the Chevron estimate as unsafe.
113
- - Do not commit/apply candidates.
114
- - Do not run `Configure -> CheckRabi` from this estimate.
115
- - Suggest `CheckQubitSpectroscopy`, coarse frequency search review, or human figure review.
116
- - Abnormally large `coarse_control_amplitude`:
117
- - Suspect the coarse search latched onto a wrong transition, poor readout contrast, or an invalid operating point.
118
- - Suggest spectroscopy/readout diagnostics rather than stronger drive.
119
- - Failed fit but visually plausible chevron:
120
- - Ask the user to inspect figures.
121
- - Consider adjusted scan bounds only with explicit user guidance.
122
- - Faint/washed-out chevron:
123
- - Suggest `ReadoutClassification`, `CheckReadoutFrequency`, and initialization/readout checks.
124
-
125
- Known failed pattern:
126
-
127
- - Q35 on mackerel / `144Qv1`, 2026-07-14:
128
- - repeated `CheckRabi` failures with `non-finite frequency: nan`
129
- - `CheckChevron` failed with `Qubit frequency too low for qid=35: 2.759894 GHz < 3.0 GHz`
130
- - input `coarse_qubit_frequency = 2.894185298591617 GHz`
131
- - input `coarse_control_amplitude = 0.5623413251903492 a.u.`
132
- - Suggested action: stop, inspect figures, and investigate spectroscopy/coarse frequency/readout state before any Configure/Rabi retry.
96
+ Use the returned task knowledge to choose the next diagnostic. Treat unsafe estimates as non-committable, stop before `Configure -> CheckRabi`, and require confirmation for any additional task.
133
97
 
134
98
  ### CheckT2Echo failures
135
99
 
136
- - Compare with recent `CheckT1` and `CheckRamsey`.
137
- - If `T2_echo` is much shorter than `2*T1`, suspect noise/refocusing issues.
138
- - Verify π/π/2 pulses before treating echo as a pure coherence problem.
100
+ Read `CheckT2Echo`, `CheckT1`, and `CheckRamsey` with `qdash_get_task_knowledge`, then compare their recent results before choosing a diagnostic.
139
101
 
140
102
  ## Coupling / two-qubit recovery rules
141
103
 
@@ -150,9 +112,7 @@ For `ZX90InterleavedRandomizedBenchmarking` or two-qubit validation failures, do
150
112
  5. `CheckBellStateTomography`
151
113
  6. then RB validation
152
114
 
153
- Treat `completed` as execution status, not proof of quality. If Bell fidelity is low, IRB uncertainty is large, or coherence-limit fidelity is much higher than measured Bell/IRB fidelity, suspect ZX90 angle/phase/cancel/rotary calibration before blaming T1/T2.
154
-
155
- If CR rotation is weak, inspect qubit detuning/frequency collisions and coupling history before increasing CR amplitude.
115
+ Treat `completed` as execution status, not proof of quality. Before interpreting Bell, tomography, coherence-limit, CR, ZX90, or IRB output, read each relevant task with `qdash_get_task_knowledge`; then correlate the returned criteria with figures and same-cooldown history.
156
116
 
157
117
  ## Session policy guidelines
158
118
 
@@ -22,9 +22,12 @@ checks.
22
22
  move together. The sensor itself may be a proxy for an unmeasured actuator,
23
23
  vibration, electrical noise, magnetic noise, or control cycle.
24
24
  - Count observed cycles, not raw sample points, when judging periodic evidence.
25
+ - Resolve cooldown boundaries before interpreting drift or shared periodicity. Analyze each cooldown separately by default; never smooth, interpolate, correlate, or fit a period across a cooldown boundary unless an explicit cross-cooldown comparison is intended and clearly reported.
25
26
 
26
27
  ## Workflow
27
28
 
29
+ When a compared QDash series comes from a task-specific output, call `qdash_get_task_knowledge` for that task before assigning physical meaning to its values, expected behavior, or failure modes.
30
+
28
31
  ### 1. Fix the investigation scope
29
32
 
30
33
  Record the reusable scope with `/qdash-investigation-setup` when multiple
@@ -32,6 +35,8 @@ comparison passes are expected. The JSON context accepts arbitrary QDash
32
35
  series, CSV mappings, window/timezone, and transform defaults. Record:
33
36
 
34
37
  - QDash profile and chip
38
+ - cooldown IDs and start/end boundaries from `qdash_list_cooldowns`
39
+ - any in-cooldown wiring events that overlap the window
35
40
  - metrics and target IDs
36
41
  - tags
37
42
  - start/end timestamps
@@ -45,7 +50,9 @@ a timezone from the host machine.
45
50
  ### 2. Inspect source integrity
46
51
 
47
52
  Before comparing, use `qdash_inspect_timeseries_csv` for local files whose
48
- schema or time encoding is not already established. Then:
53
+ schema or time encoding is not already established. Use `qdash_list_cooldowns`
54
+ to split the requested window at cooldown boundaries, and inspect cooldown
55
+ wiring events when hardware conditions may have changed. Then:
49
56
 
50
57
  - verify first/last timestamps and overlap
51
58
  - check source point counts and cadence
@@ -17,7 +17,8 @@ Use this skill when investigating coupling-level calibration quality, especially
17
17
 
18
18
  - Treat `completed` as execution status, not proof of calibration quality.
19
19
  - Diagnose from evidence before suggesting operational actions.
20
- - Prefer read-only tools first: `qdash_target_report`, `qdash_list_task_results`, `qdash_get_task_result`, `qdash_analyze_figure_json`, and `qdash_get_task_figures`.
20
+ - Resolve cooldown membership before comparing calibrations or coherence limits. Keep different cooldowns separate, and distinguish a cooldown boundary from an in-cooldown wiring change.
21
+ - Prefer read-only tools first: `qdash_list_cooldowns`, `qdash_target_report`, `qdash_list_task_results`, `qdash_get_task_result`, `qdash_analyze_figure_json`, and `qdash_get_task_figures`.
21
22
  - Do not rerun RB first when validation quality is suspicious. Walk back through prerequisites.
22
23
  - Do not commit or apply candidates without explicit user confirmation, and only after downstream validation.
23
24
 
@@ -25,8 +26,8 @@ Use this skill when investigating coupling-level calibration quality, especially
25
26
 
26
27
  For a coupling `cA-B`:
27
28
 
28
- 1. Build context with `qdash_target_report` and recent task results for the coupling.
29
- 2. Inspect the latest tasks in this chain:
29
+ 1. Build context with `qdash_list_cooldowns`, `qdash_target_report`, and recent task results for the coupling. Record the cooldown ID for each result and inspect wiring events when measurement conditions may have changed.
30
+ 2. Inspect the latest same-cooldown tasks in this chain by default, and call `qdash_get_task_knowledge` for each task before interpreting its outputs or figures:
30
31
  - `CheckCrossResonance`
31
32
  - `CreateZX90`
32
33
  - `CheckZX90`
@@ -45,51 +46,9 @@ For a coupling `cA-B`:
45
46
 
46
47
  ## Interpretation rules
47
48
 
48
- ### Completed but low quality
49
+ Use `qdash_get_task_knowledge` for `CheckCrossResonance`, `CheckZX90`, `CheckBellState`, `CheckBellStateTomography`, `Check2QGateCoherenceLimit`, and `ZX90InterleavedRandomizedBenchmarking` as applicable. Apply the returned expected-result descriptions, criteria, failure modes, and check questions to the measured outputs.
49
50
 
50
- Flag as suspicious when any of these hold:
51
-
52
- - Bell-state tomography fidelity is below ~0.8.
53
- - IRB fidelity has a large uncertainty/error bar, or `n_trials` is small.
54
- - Coherence-limit fidelity is high but Bell/IRB fidelity is much lower.
55
- - `CheckZX90` repeated-pulse traces do not show a stable/consistent response.
56
- - CR parameter generation succeeds but `cr_amplitude` is near saturation or `zx_rotation_rate` is very small.
57
-
58
- ### Coherence limit gap
59
-
60
- If `two_qubit_gate_coherence_limit` is high but measured Bell/IRB fidelity is low, do not attribute the issue primarily to T1/T2. Suspect control/calibration issues such as:
61
-
62
- - ZX90 angle error
63
- - CR phase error
64
- - cancel-pulse amplitude/phase mismatch
65
- - rotary compensation mismatch
66
- - readout/classification contribution to validation figures
67
-
68
- ### Bell tomography signals
69
-
70
- For a Bell target resembling `( |00> + |11> ) / sqrt(2)`:
71
-
72
- - Large `|10>` or `|01>` population suggests population/conditional-rotation error, not just dephasing.
73
- - Weak `|00><11|` coherence suggests phase, dephasing, or preparation error.
74
- - Large imaginary coherence can indicate phase-compensation error.
75
-
76
- Use the heatmap summaries from `qdash_analyze_figure_json` as numeric evidence. Do not overfit a single plot; correlate with `CheckZX90` and IRB.
77
-
78
- ### CheckZX90 repeated-pulse signals
79
-
80
- Look for stable repeated-pulse behavior. Warnings include:
81
-
82
- - Large scatter or non-decaying erratic points after the first few repetitions.
83
- - Large first-step contrast followed by a non-zero offset.
84
- - Different qualitative behavior between control and target qubit plots.
85
-
86
- These symptoms point toward angle/phase/cancel/rotary issues and should be investigated before trusting IRB.
87
-
88
- ### IRB caveats
89
-
90
- - A `completed` IRB with large error bar is weak evidence.
91
- - Small `n_trials` should be reported explicitly.
92
- - If Bell tomography is poor, do not use IRB alone as a pass signal.
51
+ Treat `completed` only as execution status. Correlate task knowledge with `qdash_analyze_figure_json`, same-cooldown history, and adjacent validation tasks; do not overfit one plot or use IRB alone as a pass signal.
93
52
 
94
53
  ## Recommended action sequence
95
54
 
@@ -107,19 +66,4 @@ Inspect figures/results
107
66
 
108
67
  Each operational step requires explicit confirmation. Validate Bell/tomography before using IRB as the final quality signal.
109
68
 
110
- ## Known case: mackerel 144Qv1 c48-50, 20260725-003
111
-
112
- Symptoms observed:
113
-
114
- - `CheckCrossResonance` completed with `zx_rotation_rate ~ 1.69e-3`.
115
- - `Check2QGateCoherenceLimit` reported ~0.978.
116
- - `CheckBellStateTomography` reported `bell_state_fidelity ~ 0.60`.
117
- - Bell tomography had high `|10>` component/population (~0.29) and non-ideal coherence.
118
- - `ZX90InterleavedRandomizedBenchmarking` completed with `zx90_gate_fidelity ~0.94` but large error (~0.087) and `n_trials=10`.
119
- - `CheckZX90` repeated-pulse traces were not clean/stable.
120
-
121
- Interpretation:
122
-
123
- - This is not primarily coherence-limited.
124
- - Suspect ZX90 angle/phase/cancel/rotary calibration quality.
125
- - Prefer walking back through `CheckCrossResonance -> CreateZX90 -> CheckZX90`, then validate with Bell tomography before trusting IRB.
69
+ Historical postmortem cases belong in QDash task knowledge. Retrieve them through `qdash_get_task_knowledge` rather than encoding device-specific values in this procedural skill.