From af7d811e918b1004dda4bf3e0598e200ef354cbc Mon Sep 17 00:00:00 2001 From: Codex Date: Mon, 29 Jun 2026 12:17:40 +0000 Subject: [PATCH] fix: classify MDTODO empty-detail pane gaps --- .../hwlab-node-web-observe-analyzer-source.ts | 33 +++++++++++++++---- ...node-web-observe-analyzer-timing-source.ts | 1 + scripts/src/hwlab-node-web-observe-collect.ts | 4 +-- scripts/src/hwlab-node-web-observe-render.ts | 7 ++++ 4 files changed, 37 insertions(+), 8 deletions(-) diff --git a/scripts/src/hwlab-node-web-observe-analyzer-source.ts b/scripts/src/hwlab-node-web-observe-analyzer-source.ts index bc28187f..4238c47e 100644 --- a/scripts/src/hwlab-node-web-observe-analyzer-source.ts +++ b/scripts/src/hwlab-node-web-observe-analyzer-source.ts @@ -1084,7 +1084,9 @@ function buildProjectManagementReport(samples, control, network, pagePerformance const hwpodBlockerSamples = projectManagementHwpodBlockerRows(projectSamples); const projectionReportSamples = projectManagementProjectionReportRows(projectSamples); const hwpodApiFailures = projectManagementHwpodApiFailureRows(projectApiFailures); - const severePaneGapSamples = projectManagementPaneGapRows(projectSamples); + const paneGapRows = projectManagementPaneGapRows(projectSamples); + const severePaneGapSamples = paneGapRows.actionable; + const ignoredPaneGapSamples = paneGapRows.ignored; const previewCommands = commandRows.filter((item) => item.type === "openMdtodoReportPreview" || item.type === "toggleMdtodoReportFullscreen"); const launchNonEmpty = launchSuccess.filter((item) => item.chatObserved === true && (Number(item.workbenchMessageCount ?? 0) > 0 || Number(item.workbenchTraceRowCount ?? 0) > 0)); const launchEmpty = launchSuccess.filter((item) => item.chatObserved !== true || (Number(item.workbenchMessageCount ?? 0) === 0 && Number(item.workbenchTraceRowCount ?? 0) === 0)); @@ -1128,6 +1130,7 @@ function buildProjectManagementReport(samples, control, network, pagePerformance projectionReportSampleCount: projectionReportSamples.length, hwpodApiFailureCount: hwpodApiFailures.length, severePaneGapSampleCount: severePaneGapSamples.length, + ignoredPaneGapSampleCount: ignoredPaneGapSamples.length, maxPaneBottomGapPx: maxNumber(severePaneGapSamples.map((item) => item.maxBottomGapPx)), maxPaneBottomGapRatio: maxNumber(severePaneGapSamples.map((item) => item.maxBottomGapRatio)), launchButtonVisibleSamples: launchVisibleSamples.length, @@ -1194,6 +1197,7 @@ function buildProjectManagementReport(samples, control, network, pagePerformance projectionReportSamples: projectionReportSamples.slice(0, 40), hwpodApiFailures: hwpodApiFailures.slice(0, 40), severePaneGapSamples: severePaneGapSamples.slice(0, 40), + ignoredPaneGapSamples: ignoredPaneGapSamples.slice(0, 40), projectApiPerformance, slowProjectApiPerformance, valuesRedacted: true @@ -1265,6 +1269,8 @@ function compactProjectManagementForOutput(report) { path: item?.path ?? null, selectedTaskRefHash: item?.selectedTaskRefHash ?? null, selectedFileLabelPreview: item?.selectedFileLabelPreview ?? null, + pageKind: item?.pageKind ?? null, + reason: item?.reason ?? null, severePaneCount: item?.severePaneCount ?? null, maxBottomGapPx: item?.maxBottomGapPx ?? null, maxBottomGapRatio: item?.maxBottomGapRatio ?? null, @@ -1281,6 +1287,7 @@ function compactProjectManagementForOutput(report) { projectionReportSamples: Array.isArray(report.projectionReportSamples) ? report.projectionReportSamples.slice(0, 8).map(compactSample) : [], hwpodApiFailures: Array.isArray(report.hwpodApiFailures) ? report.hwpodApiFailures.slice(0, 8).map(compactApiGroup) : [], severePaneGapSamples: Array.isArray(report.severePaneGapSamples) ? report.severePaneGapSamples.slice(0, 8).map(compactSample) : [], + ignoredPaneGapSamples: Array.isArray(report.ignoredPaneGapSamples) ? report.ignoredPaneGapSamples.slice(0, 8).map(compactSample) : [], projectApiPerformance: Array.isArray(report.projectApiPerformance) ? report.projectApiPerformance.slice(0, 8).map(compactPerformance) : [], slowProjectApiPerformance: Array.isArray(report.slowProjectApiPerformance) ? report.slowProjectApiPerformance.slice(0, 8).map(compactPerformance) : [], valuesRedacted: true @@ -1372,6 +1379,7 @@ function projectManagementSampleRef(sample) { ts: sample?.ts ?? null, pageRole: sample?.pageRole ?? null, path: sample?.path ?? null, + pageKind: sample?.projectManagement?.pageKind ?? null, selectedTaskRefHash: sample?.projectManagement?.selectedTaskRef?.hash ?? null, selectedFileLabelPreview: sample?.projectManagement?.selectedFileLabel?.textPreview ?? null, valuesRedacted: true @@ -1427,7 +1435,8 @@ function projectManagementHwpodApiFailureRows(projectApiFailures) { } function projectManagementPaneGapRows(projectSamples) { - const rows = []; + const actionable = []; + const ignored = []; for (const sample of projectSamples || []) { const paneGaps = Array.isArray(sample?.projectManagement?.paneGaps) ? sample.projectManagement.paneGaps : []; const severeGaps = paneGaps @@ -1453,16 +1462,28 @@ function projectManagementPaneGapRows(projectSamples) { const multiPane = severeGaps.length >= 2; const singleExtreme = maxGapPx >= 240 && maxGapRatio >= 0.45; if (!multiPane && !singleExtreme) continue; - rows.push({ + const selectedTaskRefHash = sample?.projectManagement?.selectedTaskRef?.hash ?? null; + const isMdtodo = sample?.projectManagement?.pageKind === "project-management-mdtodo"; + const isInitialEmptyDetail = isMdtodo && !selectedTaskRefHash; + const row = { ...projectManagementSampleRef(sample), severePaneCount: severeGaps.length, maxBottomGapPx: maxGapPx, maxBottomGapRatio: maxGapRatio, paneGaps: severeGaps.slice(0, 4), valuesRedacted: true - }); + }; + if (isInitialEmptyDetail) { + ignored.push({ + ...row, + reason: "mdtodo-initial-empty-detail-no-selected-task", + valuesRedacted: true + }); + continue; + } + actionable.push(row); } - return rows; + return { actionable, ignored, valuesRedacted: true }; } function buildProjectManagementFindings(report) { @@ -1507,7 +1528,7 @@ function buildProjectManagementFindings(report) { findings.push({ id: "mdtodo-task-count-diverged", severity: "amber", summary: "MDTODO task count varied sharply during observation; compare control commands and observer samples for projection divergence", minTaskCount: summary.mdtodoTaskCountMin, maxTaskCount: summary.mdtodoTaskCountMax, samples: report.samples.slice(-20), valuesRedacted: true }); } if (Number(summary.severePaneGapSampleCount ?? 0) > 0) { - findings.push({ id: "mdtodo-pane-bottom-gap", severity: "red", summary: "MDTODO task tree, main detail, or report sidebar left large unused bottom gaps when the visible task set was small", count: summary.severePaneGapSampleCount, maxBottomGapPx: summary.maxPaneBottomGapPx, maxBottomGapRatio: summary.maxPaneBottomGapRatio, samples: report.severePaneGapSamples.slice(0, 12), valuesRedacted: true }); + findings.push({ id: "mdtodo-pane-bottom-gap", severity: "red", summary: "MDTODO task tree, main detail, or report sidebar left large unused bottom gaps in actionable selected-task samples", count: summary.severePaneGapSampleCount, ignoredInitialEmptyDetailCount: summary.ignoredPaneGapSampleCount, maxBottomGapPx: summary.maxPaneBottomGapPx, maxBottomGapRatio: summary.maxPaneBottomGapRatio, samples: report.severePaneGapSamples.slice(0, 12), valuesRedacted: true }); } if (Number(summary.hwpodApiFailureCount ?? 0) > 0) { findings.push({ id: "project-management-hwpod-api-failed", severity: "red", summary: "HWPOD-backed MDTODO task detail or report preview API returned a server error during natural page use", count: summary.hwpodApiFailureCount, failures: report.hwpodApiFailures.slice(0, 12), valuesRedacted: true }); diff --git a/scripts/src/hwlab-node-web-observe-analyzer-timing-source.ts b/scripts/src/hwlab-node-web-observe-analyzer-timing-source.ts index 6aff730c..33d8a2f6 100644 --- a/scripts/src/hwlab-node-web-observe-analyzer-timing-source.ts +++ b/scripts/src/hwlab-node-web-observe-analyzer-timing-source.ts @@ -2344,6 +2344,7 @@ function renderMarkdown(report) { + "- latestPath: " + (projectSummary.latestPath || "-") + "\n" + "- latestCounts: source=" + (projectSummary.latestSourceCount ?? "-") + " file=" + (projectSummary.latestFileCount ?? "-") + " task=" + (projectSummary.latestTaskCount ?? "-") + "\n" + "- latestSelectedTaskRefHash: " + (projectSummary.latestSelectedTaskRefHash || "-") + "\n" + + "- paneGap: actionable=" + (projectSummary.severePaneGapSampleCount ?? 0) + " ignoredInitialEmptyDetail=" + (projectSummary.ignoredPaneGapSampleCount ?? 0) + "\n" + "- launch: commands=" + (projectSummary.launchCommandCount ?? 0) + " success=" + (projectSummary.launchSuccessCount ?? 0) + " failure=" + (projectSummary.launchFailureCount ?? 0) + " otelTraceHeader=" + (projectSummary.launchWithOtelTraceHeaderCount ?? 0) + "\n" + "- projectApi: responses=" + (projectSummary.projectApiResponseCount ?? 0) + " failures=" + (projectSummary.projectApiFailureCount ?? 0) + " requestfailed=" + (projectSummary.projectApiRequestFailedCount ?? 0) + " slowPaths=" + (projectSummary.projectApiSlowPathCount ?? 0) + "\n\n" + "### Project samples\n\n" + projectSampleLines + "\n\n" diff --git a/scripts/src/hwlab-node-web-observe-collect.ts b/scripts/src/hwlab-node-web-observe-collect.ts index d768bac1..1432ee8b 100644 --- a/scripts/src/hwlab-node-web-observe-collect.ts +++ b/scripts/src/hwlab-node-web-observe-collect.ts @@ -563,7 +563,7 @@ function projectSummaryFromSamples(){ const findings=Array.isArray(report.findings)?report.findings.filter((item)=>String(item?.id||item?.kind||'').match(/project-management|mdtodo|workbench-launch/u)).slice(0,20):[]; const summary=report.projectManagement?.summary||{}; const mdtodoSampleCount=projectSamples.filter((sample)=>sample.projectManagement?.pageKind==='project-management-mdtodo').length; - const derived={enabled:summary.enabled===true||projectSamples.length>0,projectSampleCount:Math.max(Number(summary.projectSampleCount??0),projectSamples.length),mdtodoSampleCount:Math.max(Number(summary.mdtodoSampleCount??0),mdtodoSampleCount),latestPageKind:summary.latestPageKind??latestProject?.pageKind??null,latestPath:summary.latestPath??latest?.path??null,latestSeq:summary.latestSeq??latest?.seq??null,latestTs:summary.latestTs??latest?.ts??null,latestSourceCount:summary.latestSourceCount??latestProject?.sourceCount??null,latestFileCount:summary.latestFileCount??latestProject?.fileCount??null,latestTaskCount:summary.latestTaskCount??latestProject?.taskCount??null,latestSelectedTaskRefHash:summary.latestSelectedTaskRefHash??latestProject?.selectedTaskRef?.hash??null,latestSelectedTaskRefPreview:summary.latestSelectedTaskRefPreview??latestProject?.selectedTaskRef?.preview??null,latestSelectedFileLabelPreview:summary.latestSelectedFileLabelPreview??latestProject?.selectedFileLabel?.textPreview??null,latestSelectedFileLabelLooksDirect:summary.latestSelectedFileLabelLooksDirect??latestProject?.selectedFileLabelLooksDirect??null,selectedTaskBodyVisibleSamples:summary.selectedTaskBodyVisibleSamples??projectSamples.filter((sample)=>sample.projectManagement?.taskBodyVisible===true).length,reportLinkVisibleSamples:summary.reportLinkVisibleSamples??projectSamples.filter((sample)=>Number(sample.projectManagement?.reportLinkCount??0)>0).length,reportPreviewVisibleSamples:summary.reportPreviewVisibleSamples??projectSamples.filter((sample)=>sample.projectManagement?.reportPreviewVisible===true).length,reportFullscreenVisibleSamples:summary.reportFullscreenVisibleSamples??projectSamples.filter((sample)=>sample.projectManagement?.reportFullscreenVisible===true).length,projectCommandCount:commandRows.length,mutationCommandCount:mutations.length,mutationFailureCount:mutations.filter((item)=>item.phase==='failed').length,launchCommandCount:summary.launchCommandCount??launches.length,launchSuccessCount:summary.launchSuccessCount??launches.filter((item)=>Number(item.status)>=200&&Number(item.status)<300).length,launchFailureCount:summary.launchFailureCount??launches.filter((item)=>item.phase==='failed'||Number(item.status)>=400).length,launchNonEmptyCount:summary.launchNonEmptyCount??launches.filter((item)=>item.chatObserved===true&&(Number(item.workbenchMessageCount??0)>0||Number(item.workbenchTraceRowCount??0)>0)).length,launchEmptyCount:summary.launchEmptyCount??launches.filter((item)=>item.chatObserved!==true||(Number(item.workbenchMessageCount??0)===0&&Number(item.workbenchTraceRowCount??0)===0)).length,launchWithOtelTraceHeaderCount:summary.launchWithOtelTraceHeaderCount??launches.filter((item)=>item.otelTraceId).length,projectApiResponseCount:summary.projectApiResponseCount??null,projectApiFailureCount:summary.projectApiFailureCount??null,projectApiRequestFailedCount:summary.projectApiRequestFailedCount??null,projectApiSlowPathCount:summary.projectApiSlowPathCount??null,valuesRedacted:true}; + const derived={enabled:summary.enabled===true||projectSamples.length>0,projectSampleCount:Math.max(Number(summary.projectSampleCount??0),projectSamples.length),mdtodoSampleCount:Math.max(Number(summary.mdtodoSampleCount??0),mdtodoSampleCount),latestPageKind:summary.latestPageKind??latestProject?.pageKind??null,latestPath:summary.latestPath??latest?.path??null,latestSeq:summary.latestSeq??latest?.seq??null,latestTs:summary.latestTs??latest?.ts??null,latestSourceCount:summary.latestSourceCount??latestProject?.sourceCount??null,latestFileCount:summary.latestFileCount??latestProject?.fileCount??null,latestTaskCount:summary.latestTaskCount??latestProject?.taskCount??null,latestSelectedTaskRefHash:summary.latestSelectedTaskRefHash??latestProject?.selectedTaskRef?.hash??null,latestSelectedTaskRefPreview:summary.latestSelectedTaskRefPreview??latestProject?.selectedTaskRef?.preview??null,latestSelectedFileLabelPreview:summary.latestSelectedFileLabelPreview??latestProject?.selectedFileLabel?.textPreview??null,latestSelectedFileLabelLooksDirect:summary.latestSelectedFileLabelLooksDirect??latestProject?.selectedFileLabelLooksDirect??null,selectedTaskBodyVisibleSamples:summary.selectedTaskBodyVisibleSamples??projectSamples.filter((sample)=>sample.projectManagement?.taskBodyVisible===true).length,reportLinkVisibleSamples:summary.reportLinkVisibleSamples??projectSamples.filter((sample)=>Number(sample.projectManagement?.reportLinkCount??0)>0).length,reportPreviewVisibleSamples:summary.reportPreviewVisibleSamples??projectSamples.filter((sample)=>sample.projectManagement?.reportPreviewVisible===true).length,reportFullscreenVisibleSamples:summary.reportFullscreenVisibleSamples??projectSamples.filter((sample)=>sample.projectManagement?.reportFullscreenVisible===true).length,severePaneGapSampleCount:summary.severePaneGapSampleCount??0,ignoredPaneGapSampleCount:summary.ignoredPaneGapSampleCount??0,projectCommandCount:commandRows.length,mutationCommandCount:mutations.length,mutationFailureCount:mutations.filter((item)=>item.phase==='failed').length,launchCommandCount:summary.launchCommandCount??launches.length,launchSuccessCount:summary.launchSuccessCount??launches.filter((item)=>Number(item.status)>=200&&Number(item.status)<300).length,launchFailureCount:summary.launchFailureCount??launches.filter((item)=>item.phase==='failed'||Number(item.status)>=400).length,launchNonEmptyCount:summary.launchNonEmptyCount??launches.filter((item)=>item.chatObserved===true&&(Number(item.workbenchMessageCount??0)>0||Number(item.workbenchTraceRowCount??0)>0)).length,launchEmptyCount:summary.launchEmptyCount??launches.filter((item)=>item.chatObserved!==true||(Number(item.workbenchMessageCount??0)===0&&Number(item.workbenchTraceRowCount??0)===0)).length,launchWithOtelTraceHeaderCount:summary.launchWithOtelTraceHeaderCount??launches.filter((item)=>item.otelTraceId).length,projectApiResponseCount:summary.projectApiResponseCount??null,projectApiFailureCount:summary.projectApiFailureCount??null,projectApiRequestFailedCount:summary.projectApiRequestFailedCount??null,projectApiSlowPathCount:summary.projectApiSlowPathCount??null,valuesRedacted:true}; return {summary:derived,commands:commandRows.slice(-24),mutations:mutations.slice(-16),launches:launches.slice(-12),findings,sampleRows:projectSamples.slice(-12).map((sample)=>({seq:sample.seq??null,ts:sample.ts??null,pageRole:sample.pageRole??null,path:sample.path??null,pageKind:sample.projectManagement?.pageKind??null,sourceCount:sample.projectManagement?.sourceCount??null,fileCount:sample.projectManagement?.fileCount??null,taskCount:sample.projectManagement?.taskCount??null,selectedFileLabelPreview:sample.projectManagement?.selectedFileLabel?.textPreview??null,selectedFileLabelLooksDirect:sample.projectManagement?.selectedFileLabelLooksDirect??null,selectedTaskRefHash:sample.projectManagement?.selectedTaskRef?.hash??null,selectedTaskStatus:sample.projectManagement?.selectedTaskStatus??null,taskBodyVisible:sample.projectManagement?.taskBodyVisible===true,taskBodyBytes:sample.projectManagement?.taskBody?.textBytes??0,reportLinkCount:sample.projectManagement?.reportLinkCount??0,reportPreviewVisible:sample.projectManagement?.reportPreviewVisible===true,reportPreviewBytes:sample.projectManagement?.reportPreview?.textBytes??0,reportFullscreenVisible:sample.projectManagement?.reportFullscreenVisible===true,launchButtonEnabled:sample.projectManagement?.launchButtonEnabled===true,workbenchLinkCount:sample.projectManagement?.workbenchLinkCount??0,valuesRedacted:true})),valuesRedacted:true}; } function targetNodeFromStateDir(){ @@ -573,7 +573,7 @@ function targetNodeFromStateDir(){ } function renderProjectSummary(project){ const s=project.summary||{}; - const lines=['Project MDTODO observer '+(manifest.jobId||'-'),'=======================================================','enabled='+String(s.enabled===true)+' samples='+String(s.projectSampleCount??0)+' mdtodo='+String(s.mdtodoSampleCount??0)+' latest='+String(s.latestPageKind||'-')+' path='+String(s.latestPath||'-'),'counts source='+String(s.latestSourceCount??'-')+' file='+String(s.latestFileCount??'-')+' task='+String(s.latestTaskCount??'-')+' selectedTask='+String(s.latestSelectedTaskRefHash||'-'),'fileLabel='+short(s.latestSelectedFileLabelPreview||'-',80)+' direct='+String(s.latestSelectedFileLabelLooksDirect??'-')+' bodyVisibleSamples='+String(s.selectedTaskBodyVisibleSamples??'-')+' reportPreviewSamples='+String(s.reportPreviewVisibleSamples??'-')+' reportFullscreenSamples='+String(s.reportFullscreenVisibleSamples??'-'),'commands='+String(s.projectCommandCount??0)+' mutations='+String(s.mutationCommandCount??0)+' mutationFailures='+String(s.mutationFailureCount??0),'launch commands='+String(s.launchCommandCount??0)+' success='+String(s.launchSuccessCount??0)+' failure='+String(s.launchFailureCount??0)+' nonEmpty='+String(s.launchNonEmptyCount??'-')+' empty='+String(s.launchEmptyCount??'-')+' otelTraceHeader='+String(s.launchWithOtelTraceHeaderCount??0),'api responses='+String(s.projectApiResponseCount??'-')+' failures='+String(s.projectApiFailureCount??'-')+'/'+String(s.projectApiRequestFailedCount??'-')+' slowPaths='+String(s.projectApiSlowPathCount??'-'),'','Recent samples']; + const lines=['Project MDTODO observer '+(manifest.jobId||'-'),'=======================================================','enabled='+String(s.enabled===true)+' samples='+String(s.projectSampleCount??0)+' mdtodo='+String(s.mdtodoSampleCount??0)+' latest='+String(s.latestPageKind||'-')+' path='+String(s.latestPath||'-'),'counts source='+String(s.latestSourceCount??'-')+' file='+String(s.latestFileCount??'-')+' task='+String(s.latestTaskCount??'-')+' selectedTask='+String(s.latestSelectedTaskRefHash||'-'),'fileLabel='+short(s.latestSelectedFileLabelPreview||'-',80)+' direct='+String(s.latestSelectedFileLabelLooksDirect??'-')+' bodyVisibleSamples='+String(s.selectedTaskBodyVisibleSamples??'-')+' reportPreviewSamples='+String(s.reportPreviewVisibleSamples??'-')+' reportFullscreenSamples='+String(s.reportFullscreenVisibleSamples??'-'),'paneGap actionable='+String(s.severePaneGapSampleCount??0)+' ignoredInitialEmptyDetail='+String(s.ignoredPaneGapSampleCount??0),'commands='+String(s.projectCommandCount??0)+' mutations='+String(s.mutationCommandCount??0)+' mutationFailures='+String(s.mutationFailureCount??0),'launch commands='+String(s.launchCommandCount??0)+' success='+String(s.launchSuccessCount??0)+' failure='+String(s.launchFailureCount??0)+' nonEmpty='+String(s.launchNonEmptyCount??'-')+' empty='+String(s.launchEmptyCount??'-')+' otelTraceHeader='+String(s.launchWithOtelTraceHeaderCount??0),'api responses='+String(s.projectApiResponseCount??'-')+' failures='+String(s.projectApiFailureCount??'-')+'/'+String(s.projectApiRequestFailedCount??'-')+' slowPaths='+String(s.projectApiSlowPathCount??'-'),'','Recent samples']; for(const row of project.sampleRows.slice(-12)) lines.push('#'+String(row.seq??'-')+' '+String(row.ts||'-')+' '+String(row.pageRole||'-')+' '+String(row.pageKind||'-')+' src='+String(row.sourceCount??'-')+' files='+String(row.fileCount??'-')+' tasks='+String(row.taskCount??'-')+' fileLabel='+short(row.selectedFileLabelPreview||'-',50)+' body='+String(row.taskBodyVisible===true)+' report='+String(row.reportPreviewVisible===true)+' fullscreen='+String(row.reportFullscreenVisible===true)+' selected='+String(row.selectedTaskRefHash||'-')+' launch='+String(row.launchButtonEnabled===true)+' links='+String(row.workbenchLinkCount??0)); lines.push('','Launches'); if(project.launches.length===0) lines.push('-'); diff --git a/scripts/src/hwlab-node-web-observe-render.ts b/scripts/src/hwlab-node-web-observe-render.ts index b2e07c93..d0a77659 100644 --- a/scripts/src/hwlab-node-web-observe-render.ts +++ b/scripts/src/hwlab-node-web-observe-render.ts @@ -497,6 +497,13 @@ function renderWebObserveProjectCollectTable(value: Record, col webObserveShort(webObserveText(summary.latestSelectedTaskRefHash ?? summary.latestSelectedTaskRefPreview), 32), ]]), "", + "Project layout gaps:", + webObserveTable(["ACTIONABLE", "IGNORED_INITIAL_EMPTY_DETAIL", "MEANING"], [[ + summary.severePaneGapSampleCount ?? 0, + summary.ignoredPaneGapSampleCount ?? 0, + "ignored rows do not override selected-task/control evidence", + ]]), + "", "Project command totals:", webObserveTable(["COMMANDS", "MUTATIONS", "MUTATION_FAIL", "LAUNCH", "LAUNCH_OK", "LAUNCH_FAIL", "OTEL", "API_FAIL", "SLOW>10S"], [[ collect.commandCount ?? summary.projectCommandCount,