@oqtopus-team/pi-qdash 0.1.5 → 0.1.6

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
@@ -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
 
@@ -397,6 +403,9 @@ async function defaultChipId(client: QDashClient, chipId?: string): Promise<stri
397
403
  return chipId ?? currentContext.chipId ?? client.getDefaultChipId();
398
404
  }
399
405
 
406
+ // Keep arbitrary GET access isolated for qdash_raw_get and legacy filters that are
407
+ // not represented in the generated OpenAPI surface. Normal operations should use
408
+ // QDashClient's high-level methods or client.api so upgrades remain type-checked.
400
409
  async function rawGet<T>(client: QDashClient, path: string, query?: Record<string, unknown>): Promise<T> {
401
410
  return (client as unknown as { get<T>(path: string, query?: Record<string, unknown>): Promise<T> }).get(path, cleanQuery(query));
402
411
  }
@@ -472,19 +481,6 @@ function requireValue(value: string | undefined, name: string): string {
472
481
  return value;
473
482
  }
474
483
 
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
484
  type CooldownWiringParams = {
489
485
  profile?: string;
490
486
  configPath?: string;
@@ -502,7 +498,7 @@ type WiringInsightParams = CooldownWiringParams & {
502
498
  color?: boolean;
503
499
  };
504
500
 
505
- function newestCooldown(cooldowns: CooldownRecord[]): CooldownRecord | undefined {
501
+ function newestCooldown(cooldowns: CooldownResponse[]): CooldownResponse | undefined {
506
502
  return [...cooldowns].sort((a, b) => {
507
503
  const activeDifference = Number(a.ended_at != null) - Number(b.ended_at != null);
508
504
  if (activeDifference !== 0) return activeDifference;
@@ -514,15 +510,15 @@ async function resolveCooldownWiring(params: CooldownWiringParams) {
514
510
  params = applyQDashContext(params);
515
511
  const client = await makeClient(params);
516
512
  let selection: string;
517
- let cooldown: CooldownRecord;
513
+ let cooldown: CooldownResponse;
518
514
  let resolvedChipId = params.chipId ?? currentContext.chipId;
519
515
 
520
516
  if (params.cooldownId) {
521
- cooldown = await rawGet<CooldownRecord>(client, `/cooldowns/${pathPart(params.cooldownId)}`);
517
+ cooldown = await client.api.getCooldown(params.cooldownId);
522
518
  selection = "explicit cooldownId";
523
519
  } else {
524
520
  resolvedChipId = params.chipId ?? (params.cryoId ? undefined : await defaultChipId(client));
525
- const response = await rawGet<CooldownListResponse>(client, "/cooldowns", {
521
+ const response = await client.api.listCooldowns({
526
522
  cryo_id: params.cryoId,
527
523
  chip_id: resolvedChipId,
528
524
  });
@@ -531,7 +527,7 @@ async function resolveCooldownWiring(params: CooldownWiringParams) {
531
527
  const scope = params.cryoId ? `cryo ${params.cryoId}` : `chip ${resolvedChipId}`;
532
528
  throw new Error(`No cooldown containing wiring information was found for ${scope}`);
533
529
  }
534
- cooldown = await rawGet<CooldownRecord>(client, `/cooldowns/${pathPart(selected.cooldown_id)}`);
530
+ cooldown = await client.api.getCooldown(selected.cooldown_id);
535
531
  selection = params.cryoId ? "active/newest cooldown for cryo" : "active/newest cooldown for chip";
536
532
  }
537
533
 
@@ -539,14 +535,14 @@ async function resolveCooldownWiring(params: CooldownWiringParams) {
539
535
  const wiringInfo = typeof cooldown.wiring_info === "string" ? cooldown.wiring_info.trim() : "";
540
536
  const includeBlocks = params.includeBlocks === true || !wiringInfo;
541
537
  const history = params.includeHistory
542
- ? await rawGet(client, `/cooldowns/${pathPart(cooldown.cooldown_id)}/wiring-events`, {
538
+ ? await client.api.listCooldownWiringEvents(cooldown.cooldown_id, {
543
539
  limit: params.historyLimit ?? 20,
544
540
  skip: 0,
545
541
  })
546
542
  : undefined;
547
543
  let cryostat: unknown;
548
544
  try {
549
- cryostat = await rawGet(client, `/cryostats/${pathPart(cooldown.cryo_id)}`);
545
+ cryostat = await client.api.getCryostat(cooldown.cryo_id);
550
546
  } catch {
551
547
  // Wiring remains useful on servers where cryostat detail is not accessible.
552
548
  }
@@ -594,9 +590,9 @@ async function buildDashboard(params: { profile?: string; configPath?: string; u
594
590
  const chipId = await defaultChipId(client, params.chipId);
595
591
  const [chips, openIssues, recentExecutions, failedTaskResults, provenanceStats] = await Promise.allSettled([
596
592
  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 }),
593
+ client.api.listIssues({ is_closed: false, limit }),
594
+ client.api.listExecutions({ chip_id: chipId, limit }),
595
+ client.api.listTaskResults({ chip_id: chipId, status: "failed", limit }),
600
596
  client.getProvenanceStats(),
601
597
  ]);
602
598
  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 +734,7 @@ async function buildRecentCalibrationSummary(params: { profile?: string; configP
738
734
  const chipId = await defaultChipId(client, params.chipId);
739
735
  const end = new Date();
740
736
  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 });
737
+ const payload = await client.api.listTaskResults({ chip_id: chipId, start_from: start.toISOString(), start_to: end.toISOString(), limit: params.limit ?? 30 });
742
738
  const items = arrayFromPayload(payload).filter((item): item is Record<string, unknown> => Boolean(item) && typeof item === "object");
743
739
  const byTarget = new Map<string, Record<string, unknown>[]>();
744
740
  for (const item of items) {
@@ -939,7 +935,7 @@ async function buildTargetOperationsReport(params: { profile?: string; configPat
939
935
  const [latestTarget, results, issues, forumPosts] = await Promise.all([
940
936
  kind === "qubit" ? client.getChipQubit(chipId, id) : client.getChipCoupling(chipId, id),
941
937
  client.listTaskResults({ chipId, qid, couplingId, startAt: start.toISOString(), endAt: end.toISOString(), limit }),
942
- rawGet(client, "/issues", { is_closed: false, limit: 100 }),
938
+ client.api.listIssues({ is_closed: false, limit: 100 }),
943
939
  client.listForumPosts({ chipId, status: "open", targetType: kind, targetId: id, limit }),
944
940
  ]);
945
941
  const recentResults = arrayFromPayload(results).filter((item): item is Record<string, unknown> => Boolean(item) && typeof item === "object");
@@ -1123,7 +1119,7 @@ async function buildCalibrationValidation(params: { profile?: string; configPath
1123
1119
  const task = await client.getTaskResult(params.taskId) as unknown as Record<string, unknown>;
1124
1120
  const status = firstString(task, ["status"])?.toLowerCase() ?? "unknown";
1125
1121
  const figurePaths = taskFigurePaths(task, 20);
1126
- const issuesPayload = await rawGet(client, `/task-results/${pathPart(params.taskId)}/issues`).catch(() => []);
1122
+ const issuesPayload = await client.api.getTaskResultIssues(params.taskId).catch(() => []);
1127
1123
  const issues = arrayFromPayload(issuesPayload).filter((item): item is Record<string, unknown> => Boolean(item) && typeof item === "object");
1128
1124
  const afterExecutionId = firstString(task, ["execution_id", "executionId"]);
1129
1125
  const comparison = params.beforeExecutionId && afterExecutionId
@@ -1256,10 +1252,10 @@ async function buildDashboardInsights(params: { profile?: string; configPath?: s
1256
1252
  const start = new Date(end.getTime() - (params.withinHours ?? 168) * 3600_000);
1257
1253
  const [forums, issues, failed, metrics, aiInsights] = await Promise.allSettled([
1258
1254
  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 }),
1255
+ client.api.listIssues({ is_closed: false, limit }),
1256
+ client.api.listTaskResults({ chip_id: chipId, status: "failed", start_from: start.toISOString(), start_to: end.toISOString(), limit }),
1261
1257
  client.getChipMetrics(chipId),
1262
- rawGet(client, `/chips/${pathPart(chipId)}/ai-insights`, { latest_only: true, start_at: start.toISOString(), end_at: end.toISOString() }),
1258
+ client.api.getDashboardAiInsights(chipId, { latest_only: true, start_at: start.toISOString(), end_at: end.toISOString() }),
1263
1259
  ]);
1264
1260
  const value = <T>(result: PromiseSettledResult<T>): T | undefined => result.status === "fulfilled" ? result.value : undefined;
1265
1261
  const insightMap = new Map<string, DashboardInsight>();
@@ -1485,19 +1481,19 @@ async function executeQuery(params: QDashQueryParams) {
1485
1481
  case "chip_qubit": return client.getChipQubit(await defaultChipId(client, params.chipId), requireValue(params.qid, "qid"));
1486
1482
  case "chip_couplings": return client.listChipCouplings(await defaultChipId(client, params.chipId), { limit: params.limit, offset: params.offset });
1487
1483
  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 });
1484
+ case "cryostats": return client.api.listCryostats();
1485
+ case "cryostat": return client.api.getCryostat(requireValue(params.cryoId, "cryoId"));
1486
+ case "cooldowns": return client.api.listCooldowns({ cryo_id: params.cryoId, chip_id: params.chipId });
1487
+ case "cooldown": return client.api.getCooldown(requireValue(params.cooldownId, "cooldownId"));
1488
+ case "cooldown_wiring_events": return client.api.listCooldownWiringEvents(requireValue(params.cooldownId, "cooldownId"), { limit: params.limit, skip: params.skip });
1493
1489
  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
1490
  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
1491
  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") });
1492
+ case "task_note": return client.api.getTaskNote(requireValue(params.taskId, "taskId"));
1493
+ case "task_result_issues": return client.api.getTaskResultIssues(requireValue(params.taskId, "taskId"));
1494
+ case "qubit_latest": return client.api.getLatestQubitTaskResults({ chip_id: await defaultChipId(client, params.chipId), task: requireValue(params.task ?? params.taskName, "task") });
1499
1495
  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") });
1496
+ case "coupling_latest": return client.api.getLatestCouplingTaskResults({ chip_id: await defaultChipId(client, params.chipId), task: requireValue(params.task ?? params.taskName, "task") });
1501
1497
  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
1498
  case "tasks": return client.listTasks(params.backend);
1503
1499
  case "task_knowledge": return params.taskName ? client.getTaskKnowledge(params.taskName) : client.listTaskKnowledge();
@@ -1507,23 +1503,26 @@ async function executeQuery(params: QDashQueryParams) {
1507
1503
  case "files_tree": return client.getFilesTree();
1508
1504
  case "file_content": return client.getFileContent(requireValue(params.path, "path"));
1509
1505
  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 });
1506
+ case "issues": return client.api.listIssues({ task_id: params.taskId, is_closed: params.isClosed, limit: params.limit, skip: params.skip });
1507
+ case "issue_knowledge": return client.api.listIssueKnowledge({ status: params.status, task_name: params.taskName, limit: params.limit, skip: params.skip });
1512
1508
  case "flows": return client.listFlows();
1513
1509
  case "flow": return client.getFlow(requireValue(params.flowName, "flowName"));
1514
1510
  case "flow_templates": return client.listFlowTemplates();
1515
1511
  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"))}`);
1512
+ case "flow_helper_files": return client.api.listFlowHelperFiles();
1513
+ case "flow_helper_file": return client.api.getFlowHelperFile(requireValue(params.filename, "filename"));
1518
1514
  case "executions": return rawGet(client, "/executions", { chip_id: params.chipId, flow_name: params.flowName, status: params.status, skip: params.skip, limit: params.limit });
1519
1515
  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"))}`);
1516
+ 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 });
1517
+ case "ai_review_runs": return client.api.listTaskResultAiReviewRuns({ chip_id: params.chipId, task_name: params.taskName, skip: params.skip, limit: params.limit });
1518
+ case "ai_review_run": return client.api.getTaskResultAiReviewRun(requireValue(params.reviewRunId, "reviewRunId"));
1523
1519
  case "forum_posts": return client.listForumPosts({ status: params.status, chipId: params.chipId, limit: params.limit, skip: params.skip });
1524
1520
  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 });
1521
+ case "provenance_history": return client.api.getParameterHistory({ parameter_name: requireValue(params.parameterName ?? params.parameter, "parameterName"), qid: requireValue(params.qid, "qid"), limit: params.limit });
1522
+ case "provenance_changes": {
1523
+ const parameterName = params.parameterName ?? params.parameter;
1524
+ return client.getRecentChanges({ parameterNames: parameterName ? [parameterName] : undefined, withinHours: params.withinHours, limit: params.limit });
1525
+ }
1527
1526
  case "provenance_lineage": return client.getProvenanceLineage(requireValue(params.entityId, "entityId"));
1528
1527
  case "provenance_impact": return client.getProvenanceImpact(requireValue(params.entityId, "entityId"));
1529
1528
  }
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.6",
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": "*",
@@ -76,6 +76,20 @@ Use these commands to make pi behave like a QDash-specific harness with persiste
76
76
 
77
77
  Prefer the current context when the user has already selected a profile/chip/session. Tools use that context when parameters are omitted.
78
78
 
79
+ ## Cooldown-aware investigations
80
+
81
+ Treat cooldown identity as mandatory investigation context whenever comparing calibration results, coherence metrics, drift, failures, or Forum evidence across time:
82
+
83
+ 1. Call `qdash_list_cooldowns` for the chip early in the investigation and record the relevant `cooldown_id`, `started_at`, and `ended_at` boundaries.
84
+ 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.
85
+ 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.
86
+ 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.
87
+ 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.
88
+ 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.
89
+ 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.
90
+
91
+ Before publishing investigated evidence, verify that every date-based claim respects these rules.
92
+
79
93
  ## Generic timeseries comparison
80
94
 
81
95
  Use `qdash_compare_timeseries` when the investigation needs to align multiple
@@ -116,8 +130,8 @@ If `QDASH_BASE_URL` is set and no profile is specified, the tools default to env
116
130
 
117
131
  When the user wants to preserve an investigated observation in QDash forum/notes:
118
132
 
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.
133
+ 1. Inspect the task result, task figure, timeseries/history, related Forum context, and cooldown membership first.
134
+ 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
135
  3. Prefer replying to an existing target/coupling forum thread when one exists.
122
136
  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
137
  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.
@@ -32,7 +32,8 @@ This skill defines how pi should behave as a conservative, confirmation-gated ca
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.
@@ -42,6 +43,8 @@ This skill defines how pi should behave as a conservative, confirmation-gated ca
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
@@ -22,6 +22,7 @@ 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
 
@@ -32,6 +33,8 @@ comparison passes are expected. The JSON context accepts arbitrary QDash
32
33
  series, CSV mappings, window/timezone, and transform defaults. Record:
33
34
 
34
35
  - QDash profile and chip
36
+ - cooldown IDs and start/end boundaries from `qdash_list_cooldowns`
37
+ - any in-cooldown wiring events that overlap the window
35
38
  - metrics and target IDs
36
39
  - tags
37
40
  - start/end timestamps
@@ -45,7 +48,9 @@ a timezone from the host machine.
45
48
  ### 2. Inspect source integrity
46
49
 
47
50
  Before comparing, use `qdash_inspect_timeseries_csv` for local files whose
48
- schema or time encoding is not already established. Then:
51
+ schema or time encoding is not already established. Use `qdash_list_cooldowns`
52
+ to split the requested window at cooldown boundaries, and inspect cooldown
53
+ wiring events when hardware conditions may have changed. Then:
49
54
 
50
55
  - verify first/last timestamps and overlap
51
56
  - 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:
30
31
  - `CheckCrossResonance`
31
32
  - `CreateZX90`
32
33
  - `CheckZX90`