feat: add workbench triad observe collect view

This commit is contained in:
Codex
2026-07-02 04:39:03 +00:00
parent 1b24e994f8
commit 6a7da005e4
4 changed files with 201 additions and 4 deletions
+1
View File
@@ -38,6 +38,7 @@ description: UniDesk Web 开发与浏览器验证技能。用户处理 UniDesk/H
- 截图取证必须优先配合页面的 RESTful 深链:截图命令应直接打开能恢复目标 source/file/task/report/session/run 和聚焦区域的 URL,再等待稳定 DOM 合约后截图。若目标页面缺少必要深链能力,不要先用脆弱点击/滚动流程硬截;应先增强前端深链与刷新恢复能力,再把截图能力接回 web-probe/observe/sentinel 命令。
- Issue closeout 引用 observer id、command id、stateDir/report SHA、screenshot SHA 和关键字段摘要;不要粘贴截图二进制、完整 JSON、Secret、cookie 或无界日志。
- 取证 drill-down 先用 `observe collect` / `observe analyze` 的 bounded view;需要截图路径、SHA、traceId 或 command detail 时优先查 `control.jsonl`、analysis 摘要或专用 artifact view,避免直接 `rg samples.jsonl`,因为页面快照是超长单行 JSON,容易触发输出截断和噪声。
- Workbench 三态/卡死离线调查优先用 `observe collect <id> --view workbench-triad --trace-id <trc_...>`,它把 DOM turn/message/trace rows、Final Response、Workbench REST terminal evidence、browser freeze blocker 和 analyzer findings 聚合到一个有界视图;只有需要样本细节时再用 `--finding``trace-frame` 下钻。
- Project Management/MDTODO closeout 必须区分 `control` 页和被动 `observer` 页:显式 `observe command` 的 command result、control URL 和对应截图是用户动作证据;observer 周期刷新或 stop 后根路由空态只能作为对照信号,不能覆盖 command result。涉及报告的验收要同时记录 `reportPreviewVisible``reportFullscreenVisible`、报告 deep link 和截图 SHA。
- MDTODO → Workbench launch 验收必须引用 `launchWorkbenchFromMdtodo` 的 command result,确认文件/任务选择、Workbench session、launch/chat 状态和 OTel trace header;不要只凭页面最终停在 Workbench URL 判断通过。
- `observe analyze` 的 archive/history findings 可能包含旧样本或旧规则误判;关闭用户入口问题前同时核对最新 sample 字段、DOM 几何和截图。若 top-level findings、latest sample 和 archive red 冲突,说明冲突并补工具反馈 issue,不要只凭 archive red 或单张截图下结论。
+1
View File
@@ -68,6 +68,7 @@ export function hwlabNodeWebProbeHelp(): Record<string, unknown> {
"bun scripts/cli.ts web-probe observe command webobs-xxxx --type launchWorkbenchFromMdtodo --filename 20260610-LVDT-旧过程归档.md --task R5.1 --provider dsflash-go",
"bun scripts/cli.ts web-probe observe status webobs-xxxx",
"bun scripts/cli.ts web-probe observe collect webobs-xxxx --view turn-summary",
"bun scripts/cli.ts web-probe observe collect webobs-xxxx --view workbench-triad --trace-id trc_xxxx",
"bun scripts/cli.ts web-probe observe collect webobs-xxxx --view timeline --command-id cmd-xxxx",
"bun scripts/cli.ts web-probe observe collect webobs-xxxx --view project-mdtodo-summary",
"bun scripts/cli.ts web-probe observe analyze webobs-xxxx",
+134 -3
View File
@@ -2,11 +2,11 @@
// Responsibility: Offline CLI view renderers for HWLAB web-probe observe artifacts.
import { shellQuote } from "./ssh";
export type NodeWebProbeObserveCollectView = "files" | "turn-summary" | "trace-frame" | "timeline" | "project-summary" | "project-mdtodo-summary";
export type NodeWebProbeObserveCollectView = "files" | "turn-summary" | "trace-frame" | "timeline" | "workbench-triad" | "project-summary" | "project-mdtodo-summary";
export function parseNodeWebProbeObserveCollectView(value: string): NodeWebProbeObserveCollectView {
if (value === "files" || value === "turn-summary" || value === "trace-frame" || value === "timeline" || value === "project-summary" || value === "project-mdtodo-summary") return value;
throw new Error(`web-probe observe collect --view must be files, turn-summary, trace-frame, timeline, project-summary, or project-mdtodo-summary; got ${value}`);
if (value === "files" || value === "turn-summary" || value === "trace-frame" || value === "timeline" || value === "workbench-triad" || value === "project-summary" || value === "project-mdtodo-summary") return value;
throw new Error(`web-probe observe collect --view must be files, turn-summary, trace-frame, timeline, workbench-triad, project-summary, or project-mdtodo-summary; got ${value}`);
}
export function nodeWebObserveCollectViewNodeScript(input: {
@@ -42,6 +42,8 @@ if(view==='files'){
}
const samples=readJsonl('samples.jsonl');
const control=readJsonl('control.jsonl');
const network=readJsonl('network.jsonl');
const browserProcess=readJsonl('browser-process.jsonl');
const manifest=readJson('manifest.json')||{};
const report=readJson('analysis/report.json')||{};
function unique(values){return Array.from(new Set(values.filter(Boolean)));}
@@ -568,6 +570,130 @@ function timelineView(rows){
const timelineRows=eventRows.rows.map((row)=>({ts:row.ts?short(row.ts,32):null,seq:row.seq??null,kind:row.kind,phase:short(row.phase,24),type:short(row.type,40),commandId:row.commandId?short(row.commandId,64):null,sessionId:row.sessionId?short(row.sessionId,64):null,traceId:row.traceId?short(row.traceId,64):null,summary:short(row.summary,140),valuesRedacted:true}));
return {anchor:{...anchor,from:isoOrNull(anchor.startMs),to:isoOrNull(anchor.endMs),startMs:undefined,endMs:undefined,anchorMs:undefined},window:{from:isoOrNull(window.fromMs),to:isoOrNull(window.toMs),windowMs:window.windowMs,anchorMode:window.anchorMode,valuesRedacted:true},counts:{control:control.length,samples:samples.length,commandFiles:commandFiles.length,matchedControl:eventRows.matchedControlCount,matchedSamples:eventRows.matchedSampleCount,matchedCommandFiles:eventRows.matchedCommandFileCount,shown:timelineRows.length,omitted:eventRows.omittedRowCount,valuesRedacted:true},timelineRows,renderedText:renderTimeline(anchor,window,eventRows),valuesRedacted:true};
}
function findingById(id){
return (Array.isArray(report.findings)?report.findings:[]).find((item)=>String(item?.id||item?.kind||item?.code||'')===id)||null;
}
function compactFinding(item){
if(!item)return null;
return {
id:String(item.id||item.kind||item.code||''),
severity:item.severity||item.level||null,
count:item.count??item.sampleCount??null,
summary:short(item.summary||item.message||'',220),
rootCause:item.rootCause||null,
rootCauseStatus:item.rootCauseStatus||null,
rootCauseConfidence:item.rootCauseConfidence||null,
valuesRedacted:true
};
}
function traceIdFromUrl(value){
const match=String(value||'').match(/\\/v1\\/workbench\\/(?:turns|traces)\\/(trc_[A-Za-z0-9_-]+)/u);
return match?match[1]:null;
}
function pathKindForNetwork(item){
const summary=item?.bodySummary&&typeof item.bodySummary==='object'?item.bodySummary:null;
if(summary?.pathKind)return String(summary.pathKind);
const url=String(item?.url||item?.requestUrl||'');
if(url.includes('/v1/workbench/turns/'))return 'workbench-turn';
if(url.includes('/v1/workbench/traces/')&&url.includes('/events'))return 'workbench-trace-events';
if(url.includes('/v1/workbench/sessions'))return 'workbench-sessions';
return null;
}
function networkEvidenceForTrace(traceId){
const rows=network.filter((item)=>{
const kind=pathKindForNetwork(item);
if(kind!=='workbench-turn'&&kind!=='workbench-trace-events')return false;
const ids=Array.isArray(item?.bodySummary?.traceIds)?item.bodySummary.traceIds:[traceIdFromUrl(item?.url||item?.requestUrl)];
return !traceId||ids.includes(traceId)||traceIdFromUrl(item?.url||item?.requestUrl)===traceId;
});
const responses=rows.filter((item)=>item.type==='response');
const requests=rows.filter((item)=>item.type==='request');
const failed=rows.filter((item)=>item.type==='requestfailed');
const terminalResponses=responses.filter((item)=>Number(item?.bodySummary?.terminalEvidenceCount??0)>0||Number(item?.bodySummary?.terminalStatusCount??0)>0||Number(item?.bodySummary?.finalTextByteCount??0)>0);
const byKind=(kind)=>responses.filter((item)=>pathKindForNetwork(item)===kind);
const latest=(items)=>items.slice(-1)[0]||null;
const compactNet=(item)=>item?{ts:item.ts||null,sampleSeq:item.sampleSeq??null,status:item.status??null,pathKind:pathKindForNetwork(item),traceId:traceIdFromUrl(item.url||item.requestUrl),urlPath:pathOnly(item.url||item.requestUrl),failure:item.failure||item.errorText||null,bodySummary:item.bodySummary?{terminalStatusCount:item.bodySummary.terminalStatusCount??null,runningStatusCount:item.bodySummary.runningStatusCount??null,traceEventLikeCount:item.bodySummary.traceEventLikeCount??null,finalTextByteCount:item.bodySummary.finalTextByteCount??null,terminalEvidenceCount:item.bodySummary.terminalEvidenceCount??null,valuesRedacted:true}:null,valuesRedacted:true}:null;
return {requestCount:requests.length,responseCount:responses.length,failedCount:failed.length,terminalResponseCount:terminalResponses.length,turnResponseCount:byKind('workbench-turn').length,traceEventsResponseCount:byKind('workbench-trace-events').length,latestTurnResponse:compactNet(latest(byKind('workbench-turn'))),latestTraceEventsResponse:compactNet(latest(byKind('workbench-trace-events'))),failed:failed.slice(-2).map(compactNet),valuesRedacted:true};
}
function traceDomSnapshot(traceId){
const matched=samples.filter((sample)=>!traceId||traceIdsFromSamples([sample]).includes(traceId));
const latest=matched.slice(-1)[0]||samples[samples.length-1]||null;
if(!latest)return {sampleSeq:null,ts:null,status:'unknown',traceRowCount:0,messageCount:0,turnCount:0,finalResponse:emptyFinalResponse(),pageRole:null,path:null,valuesRedacted:true};
const traceRows=(Array.isArray(latest.traceRows)?latest.traceRows:[]).filter((row)=>!traceId||row.traceId===traceId||textOf(row).includes(traceId));
const messages=(Array.isArray(latest.messages)?latest.messages:[]).filter((message)=>!traceId||message.traceId===traceId||textOf(message).includes(traceId)||!itemTraceId(message));
const turns=(Array.isArray(latest.turns)?latest.turns:[]).filter((turn)=>!traceId||turn.traceId===traceId||textOf(turn).includes(traceId));
const final=finalResponseFor([latest],traceId);
return {sampleSeq:latest.seq??null,ts:latest.ts||null,path:latest.path||pathOnly(latest.url)||pathOnly(latest.currentUrl)||null,pageRole:latest.pageRole||null,routeSessionId:latest.routeSessionId||null,activeSessionId:latest.activeSessionId||null,status:statusFor([latest],traceId),traceRowCount:traceRows.length,messageCount:messages.length,turnCount:turns.length,finalResponse:final,valuesRedacted:true};
}
function browserFreezeEvidence(){
const blockers=browserProcess.filter((item)=>item&&item.type==='browser-freeze-blocker');
const responsiveness=browserProcess.filter((item)=>item&&item.type==='browser-process-sample').flatMap((item)=>{
const pages=Array.isArray(item.pages)?item.pages:[];
return pages.map((page)=>({ts:item.ts||null,sampleSeq:item.sampleSeq??item.seq??null,pageRole:page?.pageRole||null,pageId:page?.pageId||null,responsivenessLatencyMs:page?.responsiveness?.latencyMs??null,responsivenessTimeout:page?.responsiveness?.timeout===true,cdpTimeoutCount:page?.cdp?.timeoutCount??null,cdpErrorCount:page?.cdp?.errorCount??null,valuesRedacted:true}));
}).filter((item)=>item.responsivenessTimeout===true||Number(item.responsivenessLatencyMs??0)>0||Number(item.cdpTimeoutCount??0)>0);
const compactBlocker=(item)=>{
const observed=item?.observed&&typeof item.observed==='object'?item.observed:{};
const page=item?.page&&typeof item.page==='object'?item.page:{};
const kill=item?.browserKill&&typeof item.browserKill==='object'?item.browserKill:{};
return {ts:item?.ts||null,sampleSeq:item?.sampleSeq??item?.seq??null,pageRole:item?.pageRole||page.pageRole||null,summary:short(item?.summary||'',220),rootCause:item?.rootCause||null,rootCauseStatus:item?.rootCauseStatus||null,rootCauseConfidence:item?.rootCauseConfidence||null,policySource:item?.policySource||null,fallbackAllowed:item?.fallbackAllowed===true,observerRefreshAllowed:item?.observerRefreshAllowed===true,observed:{responsivenessLatencyMs:observed.responsivenessLatencyMs??null,responsivenessTimeout:observed.responsivenessTimeout===true,cdpMetricsTimeoutCount:observed.cdpMetricsTimeoutCount??null,valuesRedacted:true},page:{pageRole:page.pageRole||null,pageId:page.pageId||null,timeoutMs:page.timeoutMs??null,responsivenessLatencyMs:page.responsivenessLatencyMs??null,responsivenessTimeout:page.responsivenessTimeout===true,cdpTimeoutCount:page.cdpTimeoutCount??null,cdpErrorCount:page.cdpErrorCount??null,valuesRedacted:true},browserKill:{ok:kill.ok===true,reason:kill.reason||null,valuesRedacted:true},valuesRedacted:true};
};
return {blockerCount:blockers.length,latestBlocker:compactBlocker(blockers.slice(-1)[0]||null),responsivenessEventCount:responsiveness.length,valuesRedacted:true};
}
function workbenchTriadView(rows){
const requestedRow=requestedTurn!==null?rows[requestedTurn-1]:null;
const traceId=requestedTraceId||requestedRow?.traceId||rows.slice().reverse().find((row)=>row.traceId)?.traceId||traceIdsFromSamples(samples).slice(-1)[0]||null;
const row=rows.find((item)=>item.traceId===traceId)||requestedRow||rows.slice(-1)[0]||null;
const findings=[
compactFinding(findingById('workbench-terminal-api-dom-not-refreshed-in-place')),
compactFinding(findingById('workbench-terminal-trace-not-hydrated-in-place')),
compactFinding(findingById('workbench-turn-state-triad-inconsistent')),
compactFinding(findingById('frontend-browser-freeze-runner-blocker')),
compactFinding(findingById('frontend-playwright-responsiveness-red')),
compactFinding(findingById('frontend-cdp-metrics-timeout-red')),
].filter(Boolean);
const networkEvidence=networkEvidenceForTrace(traceId);
const dom=traceDomSnapshot(traceId);
const freeze=browserFreezeEvidence();
const statusGap={
traceId,
turnSummaryStatus:row?.status??null,
domStatus:dom.status,
domTraceRows:dom.traceRowCount,
domFinalResponseEmpty:dom.finalResponse?.empty===true,
terminalApiEvidence:networkEvidence.terminalResponseCount>0,
terminalTurnApiEvidence:Number(networkEvidence.latestTurnResponse?.bodySummary?.terminalEvidenceCount??0)>0||Number(networkEvidence.latestTurnResponse?.bodySummary?.finalTextByteCount??0)>0,
terminalTraceEventsEvidence:Number(networkEvidence.latestTraceEventsResponse?.bodySummary?.terminalEvidenceCount??0)>0||Number(networkEvidence.latestTraceEventsResponse?.bodySummary?.traceEventLikeCount??0)>0,
browserFreezeBlocker:freeze.blockerCount>0,
valuesRedacted:true
};
const compactRow=row?{round:row.round??null,commandId:row.commandId??null,sessionId:row.sessionId??null,traceId:row.traceId??null,status:row.status??null,elapsedSeconds:row.elapsedSeconds??null,recentUpdateSeconds:row.recentUpdateSeconds??null,marks:row.marks??null,firstSeq:row.firstSeq??null,lastSeq:row.lastSeq??null,lastTs:row.lastTs??null,finalResponse:row.finalResponse??null,valuesRedacted:true}:null;
return {traceId,row:compactRow,dom,networkEvidence,freeze,findings, statusGap,renderedText:renderWorkbenchTriad({traceId,row:compactRow,dom,networkEvidence,freeze,findings,statusGap}),valuesRedacted:true};
}
function renderWorkbenchTriad(input){
const lines=['Workbench triad offline diagnosis '+(manifest.jobId||'-'),'======================================================='];
lines.push('trace='+String(input.traceId||'-')+' round='+String(input.row?.round??'-')+' turnSummary='+String(input.row?.status??'-')+' domStatus='+String(input.dom.status||'-')+' domTraceRows='+String(input.dom.traceRowCount??0)+' finalEmpty='+String(input.dom.finalResponse?.empty===true));
lines.push('dom sample seq='+String(input.dom.sampleSeq??'-')+' ts='+String(input.dom.ts||'-')+' path='+String(input.dom.path||'-')+' routeSession='+String(input.dom.routeSessionId||'-')+' activeSession='+String(input.dom.activeSessionId||'-'));
const turnSummary=input.networkEvidence.latestTurnResponse?.bodySummary||{};
const traceSummary=input.networkEvidence.latestTraceEventsResponse?.bodySummary||{};
lines.push('api evidence responses='+String(input.networkEvidence.responseCount)+' terminalResponses='+String(input.networkEvidence.terminalResponseCount)+' failed='+String(input.networkEvidence.failedCount)+' turnTerminal='+String(turnSummary.terminalEvidenceCount??'-')+' turnFinalBytes='+String(turnSummary.finalTextByteCount??'-')+' traceEvents='+String(traceSummary.traceEventLikeCount??'-')+' traceTerminal='+String(traceSummary.terminalEvidenceCount??'-'));
if(input.networkEvidence.latestTurnResponse) lines.push('latest turn API '+String(input.networkEvidence.latestTurnResponse.ts||'-')+' status='+String(input.networkEvidence.latestTurnResponse.status??'-')+' sample='+String(input.networkEvidence.latestTurnResponse.sampleSeq??'-')+' path='+String(input.networkEvidence.latestTurnResponse.urlPath||'-'));
if(input.networkEvidence.latestTraceEventsResponse) lines.push('latest trace events API '+String(input.networkEvidence.latestTraceEventsResponse.ts||'-')+' status='+String(input.networkEvidence.latestTraceEventsResponse.status??'-')+' sample='+String(input.networkEvidence.latestTraceEventsResponse.sampleSeq??'-')+' path='+String(input.networkEvidence.latestTraceEventsResponse.urlPath||'-'));
if(input.freeze.latestBlocker) lines.push('browser freeze '+String(input.freeze.latestBlocker.ts||'-')+' sample='+String(input.freeze.latestBlocker.sampleSeq??'-')+' rootCause='+String(input.freeze.latestBlocker.rootCause||'-')+' status='+String(input.freeze.latestBlocker.rootCauseStatus||'-')+' fallbackAllowed='+String(input.freeze.latestBlocker.fallbackAllowed===true));
lines.push('','Findings');
if(input.findings.length===0) lines.push('-');
for(const finding of input.findings) lines.push(String(finding.severity||'-')+' '+String(finding.id||'-')+' count='+String(finding.count??'-')+' root='+String(finding.rootCause||finding.rootCauseStatus||'-')+' '+short(finding.summary||'',150));
if(input.networkEvidence.failed.length>0){
lines.push('','Network failed tail');
for(const item of input.networkEvidence.failed) lines.push(String(item.ts||'-')+' '+String(item.pathKind||'-')+' '+String(item.failure||'-')+' '+String(item.urlPath||'-'));
}
const id=manifest.jobId||'<observer>';
lines.push('','NEXT');
if(input.traceId) lines.push(' trace-frame: bun scripts/cli.ts web-probe observe collect '+id+' --view trace-frame --trace-id '+input.traceId);
if(input.traceId) lines.push(' otel: bun scripts/cli.ts platform-infra observability diagnose-code-agent --target <node> --business-trace-id '+input.traceId);
lines.push(' findings: bun scripts/cli.ts web-probe observe collect '+id+' --view files --file analysis/report.json --finding workbench-terminal-api-dom-not-refreshed-in-place');
lines.push('','DISCLOSURE source=existing artifacts valuesRedacted=true; this view does not start browser/probe or mutate runtime.');
return lines.join('\\n');
}
function pathOnly(value){try{return value?new URL(String(value),'http://x').pathname:null}catch{return null}}
function projectSummaryFromSamples(){
const projectSamples=samples.filter((sample)=>sample?.projectManagement&&typeof sample.projectManagement==='object');
@@ -643,6 +769,11 @@ if(view==='timeline'){
console.log(JSON.stringify({ok:true,command:'web-probe-observe collect',view,stateDir:dir,artifactFileCount:files.length,skippedFileCount:skippedFiles.length,skippedFiles:skippedFiles.slice(0,20),...timeline,sourceFiles:['samples.jsonl','control.jsonl','commands/pending/*.json','commands/processing/*.json','commands/done/*.json','commands/failed/*.json','commands/abandoned/*.json'],valuesRedacted:true}));
process.exit(0);
}
if(view==='workbench-triad'){
const triad=workbenchTriadView(rows);
console.log(JSON.stringify({ok:true,command:'web-probe-observe collect',view,stateDir:dir,artifactFileCount:files.length,skippedFileCount:skippedFiles.length,skippedFiles:skippedFiles.slice(0,8),traceId:triad.traceId,dom:triad.dom,networkEvidence:triad.networkEvidence,freeze:triad.freeze,findings:triad.findings,statusGap:triad.statusGap,renderedText:triad.renderedText,sourceFiles:['samples.jsonl','network.jsonl','browser-process.jsonl','analysis/report.json'],valuesRedacted:true}));
process.exit(0);
}
const frame=renderTraceFrame(selectSample(rows),rows);
console.log(JSON.stringify({ok:frame.ok!==false,command:'web-probe-observe collect',view,stateDir:dir,artifactFileCount:files.length,skippedFileCount:skippedFiles.length,skippedFiles:skippedFiles.slice(0,20),...frame,sourceFiles:['samples.jsonl','control.jsonl','analysis/report.json'],valuesRedacted:true},null,2));
`)} "$state_dir"`;
+65 -1
View File
@@ -2096,6 +2096,65 @@ function compactObserveCollectForRaw(collect: Record<string, unknown> | null): R
valuesRedacted: true,
};
}) : undefined;
const collectView = typeof collect.view === "string" ? collect.view : null;
const slimNetworkEvidence = (value: unknown): Record<string, unknown> => {
const source = observeRecord(value);
const latestTurn = observeRecord(source.latestTurnResponse);
const latestTrace = observeRecord(source.latestTraceEventsResponse);
return {
requestCount: source.requestCount ?? null,
responseCount: source.responseCount ?? null,
failedCount: source.failedCount ?? null,
terminalResponseCount: source.terminalResponseCount ?? null,
turnResponseCount: source.turnResponseCount ?? null,
traceEventsResponseCount: source.traceEventsResponseCount ?? null,
latestTurnResponse: {
ts: latestTurn.ts ?? null,
sampleSeq: latestTurn.sampleSeq ?? null,
status: latestTurn.status ?? null,
bodySummary: observeRecord(latestTurn.bodySummary),
valuesRedacted: true,
},
latestTraceEventsResponse: {
ts: latestTrace.ts ?? null,
sampleSeq: latestTrace.sampleSeq ?? null,
status: latestTrace.status ?? null,
bodySummary: observeRecord(latestTrace.bodySummary),
valuesRedacted: true,
},
valuesRedacted: true,
};
};
const slimFreeze = (value: unknown): Record<string, unknown> => {
const source = observeRecord(value);
const blocker = observeRecord(source.latestBlocker);
return {
blockerCount: source.blockerCount ?? null,
responsivenessEventCount: source.responsivenessEventCount ?? null,
latestBlocker: {
ts: blocker.ts ?? null,
sampleSeq: blocker.sampleSeq ?? null,
rootCause: blocker.rootCause ?? null,
rootCauseStatus: blocker.rootCauseStatus ?? null,
fallbackAllowed: blocker.fallbackAllowed === true,
observed: observeRecord(blocker.observed),
page: observeRecord(blocker.page),
valuesRedacted: true,
},
valuesRedacted: true,
};
};
const slimFindings = Array.isArray(collect.findings) ? collect.findings.slice(0, 6).map((item) => {
const row = observeRecord(item);
return {
id: row.id ?? null,
severity: row.severity ?? null,
count: row.count ?? null,
rootCause: row.rootCause ?? row.rootCauseStatus ?? null,
summary: typeof row.summary === "string" ? row.summary.slice(0, 140) : row.summary ?? null,
valuesRedacted: true,
};
}) : undefined;
return {
ok: collect.ok !== false,
command: collect.command,
@@ -2110,12 +2169,17 @@ function compactObserveCollectForRaw(collect: Record<string, unknown> | null): R
counts: observeRecord(collect.counts),
...(rows === undefined ? {} : { rows }),
...(timelineRows === undefined ? {} : { timelineRows }),
renderedText: collect.view === "timeline" ? undefined : typeof collect.renderedText === "string" ? collect.renderedText : undefined,
renderedText: collectView === "timeline" || collectView === "workbench-triad" ? undefined : typeof collect.renderedText === "string" ? collect.renderedText : undefined,
sourceFiles: Array.isArray(collect.sourceFiles) ? collect.sourceFiles : undefined,
blocker: collect.blocker,
sampleSeq: collect.sampleSeq,
traceId: collect.traceId,
finalResponse: collect.finalResponse,
statusGap: observeRecord(collect.statusGap),
dom: observeRecord(collect.dom),
networkEvidence: collectView === "workbench-triad" ? slimNetworkEvidence(collect.networkEvidence) : observeRecord(collect.networkEvidence),
freeze: collectView === "workbench-triad" ? slimFreeze(collect.freeze) : observeRecord(collect.freeze),
findings: collectView === "workbench-triad" ? slimFindings : Array.isArray(collect.findings) ? collect.findings.slice(0, 8) : undefined,
valuesRedacted: true,
};
}