From 00b167b68d5c33b862647b81edef7af45d62ad16 Mon Sep 17 00:00:00 2001 From: Lyon <88232613+pikasTech@users.noreply.github.com> Date: Fri, 26 Jun 2026 00:24:55 +0800 Subject: [PATCH] fix: bound mdtodo collect output (#910) Co-authored-by: Codex --- scripts/src/hwlab-node-web-observe-collect.ts | 9 +- scripts/src/hwlab-node-web-observe-render.ts | 109 ++++++++++++++++++ 2 files changed, 117 insertions(+), 1 deletion(-) diff --git a/scripts/src/hwlab-node-web-observe-collect.ts b/scripts/src/hwlab-node-web-observe-collect.ts index a24c0a85..8cf4fd5e 100644 --- a/scripts/src/hwlab-node-web-observe-collect.ts +++ b/scripts/src/hwlab-node-web-observe-collect.ts @@ -378,7 +378,14 @@ function renderProjectSummary(project){ const rows=turnSummaryRows(); if(view==='project-summary'||view==='project-mdtodo-summary'){ const project=projectSummaryFromSamples(); - console.log(JSON.stringify({ok:true,command:'web-probe-observe collect',view,stateDir:dir,summary:project.summary,sampleRowCount:project.sampleRows.length,commandCount:project.commands.length,mutationCount:project.mutations.length,launchCount:project.launches.length,findingCount:project.findings.length,commands:project.commands,mutations:project.mutations,launches:project.launches,renderedText:renderProjectSummary(project),sourceFiles:['samples.jsonl','control.jsonl','analysis/report.json'],valuesRedacted:true})); + const projectSummary={...project.summary,latestSelectedTaskRefPreview:short(project.summary?.latestSelectedTaskRefPreview,80)}; + const projectSampleRows=project.sampleRows.slice(-4).map((item)=>({seq:item.seq??null,ts:short(item.ts,24),pageRole:short(item.pageRole,18),path:short(item.path,36),pageKind:short(item.pageKind,28),sourceCount:item.sourceCount??null,fileCount:item.fileCount??null,taskCount:item.taskCount??null,selectedTaskRefHash:short(item.selectedTaskRefHash,24),selectedTaskStatus:short(item.selectedTaskStatus,20),launchButtonEnabled:item.launchButtonEnabled===true,workbenchLinkCount:item.workbenchLinkCount??0,valuesRedacted:true})); + const compactCommand=(item)=>({ts:short(item.ts,24),phase:item.phase??null,type:short(item.type,28),commandId:short(item.commandId,24),afterPath:short(item.afterPath,36),selectedTaskHash:short(item.selectedTaskHash,24),status:item.status??null,message:item.message?short(item.message,80):null,valuesRedacted:true}); + const projectCommands=project.commands.slice(-14).map(compactCommand); + const projectMutations=project.mutations.slice(-10).map(compactCommand); + const projectLaunches=project.launches.slice(-4).map((item)=>({ts:short(item.ts,24),phase:item.phase??null,commandId:short(item.commandId,24),status:item.status??null,sessionId:short(item.sessionId,28),workbenchUrl:short(item.workbenchUrl,52),otelTraceId:short(item.otelTraceId,28),taskHash:short(item.taskHash,24),message:item.message?short(item.message,80):null,valuesRedacted:true})); + const projectFindings=project.findings.slice(0,4).map((item)=>({severity:item.severity??item.level??null,id:short(item.id??item.kind??item.code,48),count:item.count??item.sampleCount??null,summary:short(item.summary??item.message,96),valuesRedacted:true})); + console.log(JSON.stringify({ok:true,command:'web-probe-observe collect',view,stateDir:dir,summary:projectSummary,sampleRowCount:project.sampleRows.length,commandCount:project.commands.length,mutationCount:project.mutations.length,launchCount:project.launches.length,findingCount:project.findings.length,sampleRows:projectSampleRows,commands:projectCommands,mutations:projectMutations,launches:projectLaunches,findings:projectFindings,sourceFiles:['samples.jsonl','control.jsonl','analysis/report.json'],valuesRedacted:true})); process.exit(0); } if(view==='turn-summary'){ diff --git a/scripts/src/hwlab-node-web-observe-render.ts b/scripts/src/hwlab-node-web-observe-render.ts index 2a49ca04..20d3cc62 100644 --- a/scripts/src/hwlab-node-web-observe-render.ts +++ b/scripts/src/hwlab-node-web-observe-render.ts @@ -243,6 +243,10 @@ function renderWebObserveCollectTable(value: Record): string { " collect views are rendered from existing samples/control/analysis artifacts; they do not create a new sampling source.", ].join("\n"); } + const collectView = webObserveText(collect?.view ?? value.view); + if (collect !== null && (collectView === "project-summary" || collectView === "project-mdtodo-summary")) { + return renderWebObserveProjectCollectTable(value, collect); + } const result = record(value.result); const file = record(collect?.file); @@ -455,6 +459,111 @@ function renderWebObserveCollectTable(value: Record): string { return lines.join("\n"); } +function renderWebObserveProjectCollectTable(value: Record, collect: Record): string { + const summary = record(collect.summary); + const sampleRows = Array.isArray(collect.sampleRows) ? collect.sampleRows.map(record).filter((item): item is Record => item !== null).slice(-6) : []; + const commands = Array.isArray(collect.commands) ? collect.commands.map(record).filter((item): item is Record => item !== null).slice(-14) : []; + const mutations = Array.isArray(collect.mutations) ? collect.mutations.map(record).filter((item): item is Record => item !== null).slice(-10) : []; + const launches = Array.isArray(collect.launches) ? collect.launches.map(record).filter((item): item is Record => item !== null).slice(-4) : []; + const findings = Array.isArray(collect.findings) ? collect.findings.map(record).filter((item): item is Record => item !== null).slice(0, 6) : []; + const lines = [ + `hwlab nodes web-probe observe collect (${webObserveText(value.status)})`, + "", + ...renderWebObserveWrapperContract(value), + webObserveTable(["ID", "NODE", "LANE", "VIEW", "STATE_DIR"], [[ + value.id, + value.node, + value.lane, + collect.view ?? value.view, + webObserveShort(webObserveText(collect.stateDir), 88), + ]]), + "", + "Project MDTODO summary:", + webObserveTable(["ENABLED", "SAMPLES", "MDTODO", "LATEST", "SRC", "FILES", "TASKS", "SELECTED"], [[ + summary.enabled, + summary.projectSampleCount, + summary.mdtodoSampleCount, + webObserveShort(`${webObserveText(summary.latestPageKind)} ${webObserveText(summary.latestPath)}`, 44), + summary.latestSourceCount, + summary.latestFileCount, + summary.latestTaskCount, + webObserveShort(webObserveText(summary.latestSelectedTaskRefHash ?? summary.latestSelectedTaskRefPreview), 32), + ]]), + "", + "Project command totals:", + webObserveTable(["COMMANDS", "MUTATIONS", "MUTATION_FAIL", "LAUNCH", "LAUNCH_OK", "LAUNCH_FAIL", "OTEL", "API_FAIL", "SLOW>10S"], [[ + collect.commandCount ?? summary.projectCommandCount, + collect.mutationCount ?? summary.mutationCommandCount, + summary.mutationFailureCount, + collect.launchCount ?? summary.launchCommandCount, + summary.launchSuccessCount, + summary.launchFailureCount, + summary.launchWithOtelTraceHeaderCount, + `${webObserveText(summary.projectApiFailureCount)}/${webObserveText(summary.projectApiRequestFailedCount)}`, + summary.projectApiSlowPathCount, + ]]), + "", + "Recent project samples:", + webObserveTable(["SEQ", "TS", "ROLE", "PATH", "KIND", "SRC", "FILES", "TASKS", "STATUS", "LAUNCH"], sampleRows.length > 0 ? sampleRows.map((item) => [ + item.seq, + webObserveShort(webObserveText(item.ts), 24), + webObserveShort(webObserveText(item.pageRole), 18), + webObserveShort(webObserveText(item.path), 24), + webObserveShort(webObserveText(item.pageKind), 28), + item.sourceCount, + item.fileCount, + item.taskCount, + webObserveShort(webObserveText(item.selectedTaskStatus), 20), + item.launchButtonEnabled === true ? "yes" : "no", + ]) : [["-", "-", "-", "-", "-", "-", "-", "-", "-", "-"]]), + "", + "Project commands:", + webObserveTable(["TS", "PHASE", "TYPE", "STATUS", "PATH", "TASK", "MESSAGE"], commands.length > 0 ? commands.map((item) => [ + webObserveShort(webObserveText(item.ts), 24), + item.phase, + webObserveShort(webObserveText(item.type), 28), + item.status, + webObserveShort(webObserveText(item.afterPath), 36), + webObserveShort(webObserveText(item.selectedTaskHash), 24), + webObserveShort(webObserveText(item.message), 72), + ]) : [["-", "-", "-", "-", "-", "-", "-"]]), + "", + "MDTODO mutations:", + webObserveTable(["TS", "PHASE", "TYPE", "STATUS", "TASK", "MESSAGE"], mutations.length > 0 ? mutations.map((item) => [ + webObserveShort(webObserveText(item.ts), 24), + item.phase, + webObserveShort(webObserveText(item.type), 28), + item.status, + webObserveShort(webObserveText(item.selectedTaskHash), 24), + webObserveShort(webObserveText(item.message), 72), + ]) : [["-", "-", "-", "-", "-", "-"]]), + "", + "Workbench launches:", + webObserveTable(["TS", "PHASE", "STATUS", "SESSION", "OTEL_TRACE", "TASK", "URL"], launches.length > 0 ? launches.map((item) => [ + webObserveShort(webObserveText(item.ts), 24), + item.phase, + item.status, + webObserveShort(webObserveText(item.sessionId), 28), + webObserveShort(webObserveText(item.otelTraceId), 28), + webObserveShort(webObserveText(item.taskHash), 24), + webObserveShort(webObserveText(item.workbenchUrl), 52), + ]) : [["-", "-", "-", "-", "-", "-", "-"]]), + "", + "Findings:", + webObserveTable(["SEVERITY", "ID", "COUNT", "SUMMARY"], findings.length > 0 ? findings.map((item) => [ + webObserveShort(webObserveText(item.severity ?? item.level), 12), + webObserveShort(webObserveText(item.id ?? item.kind ?? item.code), 48), + item.count ?? item.sampleCount, + webObserveShort(webObserveText(item.summary ?? item.message), 96), + ]) : [["-", "-", "-", "-"]]), + "", + "Disclosure:", + " collect project views are rendered from existing samples/control/analysis artifacts; they do not create a new sampling source.", + " row lists are bounded; use observe analyze or collect --file for artifact-level drill-down.", + ]; + return lines.join("\n"); +} + function webObserveTable(headers: string[], rows: unknown[][]): string { const stringRows = rows.map((row) => headers.map((_, index) => webObserveCell(row[index]))); const widths = headers.map((header, index) => Math.max(header.length, ...stringRows.map((row) => row[index]?.length ?? 0)));