diff --git a/docs/reference/observability.md b/docs/reference/observability.md index e3c4de97..e31d1a81 100644 --- a/docs/reference/observability.md +++ b/docs/reference/observability.md @@ -10,6 +10,17 @@ UniDesk 的可观测性优先级高于静默成功。CLI、服务日志、Docker 因此,CLI、Web、trace 和 issue 评论中的状态字段必须避免把“已观测到缺口”“已显示失败原因”“已提供诊断入口”表达成“问题已解决”。如果当前阶段只能增加可见性,输出应明确标记为诊断进展或阻塞定位;不得关闭对应功能 issue,不得把观测增强当作验收通过。 +## Zero Implicit Fallback + +运行时异常、投影写入异常、序列化异常、ReferenceError/TypeError、不可达上游、provider 不可用、trace 同步失败和状态机非法转移不得被隐式 fallback 吞掉。任何 catch 后继续返回旧值、空值、默认值、legacy 查询、二次来源仲裁或“看起来成功”的行为,都必须改成以下两类之一: + +- `hard failure`: 当前用户路径必须失败,并把 trace_id、错误类型、错误消息、阶段和可重试性透传到 CLI/Web/API 诊断面。 +- `observed degraded`: 只有业务明确允许降级时才可继续,但必须写入 OTel span event/error、结构化日志、诊断字段和用户可见降级提示;提示必须说明缺失的真实能力,不能把降级输出伪装成正常结果。 + +禁止用下游 repair、多来源投票、旧缓存补洞、空数组/空对象、默认标题、默认耗时、默认 final response、legacy session store 或浏览器端再计算来掩盖上游异常。尤其是 Workbench/AgentRun/WebProbe 链路中,如果 durable projection、message facts、timing facts、terminal event 或 final response 持久化失败,必须让上游写路径失败并产生 OTel 证据,而不是让前端继续用 fallback 渲染。 + +每次发现隐式 fallback,都应优先修上游 source of truth:先定位第一个吞错点,删除吞错 fallback,补 OTel/error 透传,再复测原入口。只有确认上游事实已经正确产生后,才允许清理前端展示或 CLI 表格。不得通过增加更多采样器判断、前端兜底字段或 analyzer 自动仲裁来“修好”业务结果。 + ## CLI Logs 异步 job 的 stdout 和 stderr 位于 `.state/jobs/`。`job list` 默认只返回最新 50 条摘要,并为已知异步工作流返回轻量 `progress.summary`;`job status` 会返回结构化 `progress` 与有限尾部,避免输出爆炸,同时保留完整日志文件路径便于继续排查。实现必须只读取日志尾部字节,不得先把完整 job 日志读入 CLI 内存;长时命令的阶段、关键对象名和下一步查询命令应优先沉淀到 `progress`,不能要求调用者先阅读完整日志才能知道是否卡在提交、构建、发布或观测阶段。 diff --git a/scripts/src/hwlab-node-impl.ts b/scripts/src/hwlab-node-impl.ts index b091543d..ad764a4f 100644 --- a/scripts/src/hwlab-node-impl.ts +++ b/scripts/src/hwlab-node-impl.ts @@ -10404,7 +10404,7 @@ const slimFindingSample=(value)=>{ sessionId:short(value.sessionId??value.routeSessionId??value.activeSessionId??value.controlSessionId??value.observerSessionId??refValue.routeSessionId??refValue.activeSessionId,64), path:short(value.path??value.urlPath??value.url??value.controlPath??value.observerPath??refValue.url,120), trace:short(trace,120), - detail:short(value.detail??(value.fromDigest||value.toDigest?'digest '+String(value.fromDigest??'-')+' -> '+String(value.toDigest??'-'):undefined)??(value.messageTextDigestDiff?'messageDigest '+String(value.controlMessageDigest??'-')+' != '+String(value.observerMessageDigest??'-'):undefined)??value.summary??value.message??value.preview??value.expectedPattern,180), + detail:short(value.detail??(value.durationText!==undefined||value.activityText!==undefined||value.textPreview!==undefined?['duration='+String(value.durationText??'-'),'activity='+String(value.activityText??'-'),'status='+String(value.status??'-'),'preview='+String(value.textPreview??'-')].join(' '):undefined)??(value.fromDigest||value.toDigest?'digest '+String(value.fromDigest??'-')+' -> '+String(value.toDigest??'-'):undefined)??(value.messageTextDigestDiff?'messageDigest '+String(value.controlMessageDigest??'-')+' != '+String(value.observerMessageDigest??'-'):undefined)??value.summary??value.message??value.preview??value.expectedPattern,180), valuesRedacted:true }; }; diff --git a/scripts/src/hwlab-node-web-observe-runner-source.ts b/scripts/src/hwlab-node-web-observe-runner-source.ts index eccec717..3ff8b05f 100644 --- a/scripts/src/hwlab-node-web-observe-runner-source.ts +++ b/scripts/src/hwlab-node-web-observe-runner-source.ts @@ -5538,6 +5538,9 @@ function buildCodeAgentCardTimingMetrics(samples, timeline, turnTiming) { status: card.status ?? null, messageId: card.messageId ?? null, traceId: card.traceId ?? firstTraceId([text]), + sessionId: card.sessionId ?? sample.sessionId ?? sample.workbenchSessionId ?? sample.routeSessionId ?? sample.activeSessionId ?? null, + durationText: limitText(card.durationText, 120), + activityText: limitText(card.activityText, 120), totalElapsedSeconds: totalElapsedValues.length > 0 ? Math.max(...totalElapsedValues) : null, recentUpdateSeconds: recentUpdateValues.length > 0 ? Math.max(...recentUpdateValues) : null, terminal,