fix: tidy opencode smoke post-task guidance

This commit is contained in:
Codex
2026-06-30 11:29:46 +00:00
parent 2c6b2bf6ae
commit 8fb5233bae
5 changed files with 25 additions and 8 deletions
+15 -4
View File
@@ -88,12 +88,12 @@ OTel trace 内常见业务关联属性:
OpenCode 对话长时间显示 `Thinking`、用户发消息后无 assistant 文本、或怀疑 iframe/live state 没吃到事件时,先分别证明 provider、Cloud Web 代理和浏览器事件流三层状态。provider span 可能已经 200,但 UI 仍可能因 `/global/event` 目录、ticket 或 live state 不一致而不收敛。 OpenCode 对话长时间显示 `Thinking`、用户发消息后无 assistant 文本、或怀疑 iframe/live state 没吃到事件时,先分别证明 provider、Cloud Web 代理和浏览器事件流三层状态。provider span 可能已经 200,但 UI 仍可能因 `/global/event` 目录、ticket 或 live state 不一致而不收敛。
优先用明确 service 的 TraceQL 查询 provider,避免普通 grep 漏掉新 span 名或属性 优先用增强后的 `--grep` 查询 provider/service 线索;当前 grep 会先推断合适的 TraceQL 候选查询,再在扫描到的 trace 内匹配 raw body、span name、route、status message 和 full span attributes
```bash ```bash
bun scripts/cli.ts platform-infra observability search \ bun scripts/cli.ts platform-infra observability search \
--target <node> \ --target <node> \
--query '{ resource.service.name = "opencode-provider-proxy" }' \ --grep opencode-provider-proxy \
--lookback-minutes 30 \ --lookback-minutes 30 \
--candidate-limit 100 \ --candidate-limit 100 \
--limit 20 --limit 20
@@ -106,7 +106,18 @@ Cloud Web 侧查 `/global/event` 长连接的 start span;长连接可能在调
```bash ```bash
bun scripts/cli.ts platform-infra observability search \ bun scripts/cli.ts platform-infra observability search \
--target <node> \ --target <node> \
--query '{ resource.service.name = "hwlab-cloud-web" && span.http.route = "/global/event" }' \ --grep opencode.proxy.stream.start \
--lookback-minutes 30 \
--candidate-limit 300 \
--limit 20
```
属性 drill-down 可直接查 key/value;例如:
```bash
bun scripts/cli.ts platform-infra observability search \
--target <node> \
--grep opencode.proxy.sse.directory_rewrite_enabled=true \
--lookback-minutes 30 \ --lookback-minutes 30 \
--candidate-limit 300 \ --candidate-limit 300 \
--limit 20 --limit 20
@@ -114,7 +125,7 @@ bun scripts/cli.ts platform-infra observability search \
期望看到 `opencode.proxy.stream.start`,并检查 `opencode.proxy.sse.directory_rewrite_enabled``opencode.proxy.sse.directory_rewrite_from``opencode.proxy.sse.directory_rewrite_to``opencode.proxy.ticket_accepted``span.http.route=/global/event` 和 streaming 标记。`from=/workspace``to=/` 代表 HWLAB iframe/public route 与 OpenCode server workspace route 已对齐;如果该 rewrite 不存在或 ticket 未接受,provider 正常也不能说明 UI 正常。 期望看到 `opencode.proxy.stream.start`,并检查 `opencode.proxy.sse.directory_rewrite_enabled``opencode.proxy.sse.directory_rewrite_from``opencode.proxy.sse.directory_rewrite_to``opencode.proxy.ticket_accepted``span.http.route=/global/event` 和 streaming 标记。`from=/workspace``to=/` 代表 HWLAB iframe/public route 与 OpenCode server workspace route 已对齐;如果该 rewrite 不存在或 ticket 未接受,provider 正常也不能说明 UI 正常。
关闭 OpenCode UI 卡住类 issue 时,OTel 只能证明链路状态;最终还要用 web-probe DOM/事件证据确认浏览器看到 assistant 文本、没有残留 `Thinking`,并且 EventSource 收到 `message.part.updated``step-finish``session.idle` 等事件。若 `--grep` 没搜到新属性或 span 名,改用上述 TraceQL query,再只对小范围 trace 使用 `trace --raw` 做 bounded attr drill-down。 关闭 OpenCode UI 卡住类 issue 时,OTel 只能证明链路状态;最终还要用 `web-probe opencode-smoke` 或等价 DOM/事件证据确认浏览器看到 assistant 文本、没有残留 `Thinking`,并且 EventSource 收到 `message.part.updated``step-finish``session.idle` 等事件。若 `--grep` 没搜到新属性或 span 名,先阅读输出中的 `grepCoverage``grepQueryInference` 和 no-match Next;必要时再改用显式 TraceQL,例如 `{ resource.service.name = "hwlab-cloud-web" && .http.route = "/global/event" }`,并只对小范围 trace 使用 `trace --full|--raw` 做 bounded attr drill-down。
## Code Agent / AgentRun 排障 ## Code Agent / AgentRun 排障
@@ -170,7 +170,8 @@ MDTODO 或 Project Management 的 Web 重写/布局 closeout 不能只引用组
- issue closeout 优先引用 `web-probe script` 输出的顶层 `issueEvidence``summary.issueEvidence`;只有需要展开调查时才粘贴 `probe.script.result``probe.steps` 或完整 `reportPath`,避免 stdout、summary 和 report 多层重复同一证据。 - issue closeout 优先引用 `web-probe script` 输出的顶层 `issueEvidence``summary.issueEvidence`;只有需要展开调查时才粘贴 `probe.script.result``probe.steps` 或完整 `reportPath`,避免 stdout、summary 和 report 多层重复同一证据。
- stdin heredoc 与 `--script-file` 都按 ES module 加载,脚本必须导出 `export default async ({ page, gotoStable, recordStep, ... }) => { ... }`;不要在模块顶层直接写 `return`。失败为 `Illegal return statement``does not provide an export named default` 或 finalUrl 仍是 `about:blank` 且 stepCount=0 时,先按 probe 脚本入口误用处理,不要归因成 Cloud Web 行为失败。 - stdin heredoc 与 `--script-file` 都按 ES module 加载,脚本必须导出 `export default async ({ page, gotoStable, recordStep, ... }) => { ... }`;不要在模块顶层直接写 `return`。失败为 `Illegal return statement``does not provide an export named default` 或 finalUrl 仍是 `about:blank` 且 stepCount=0 时,先按 probe 脚本入口误用处理,不要归因成 Cloud Web 行为失败。
- 自定义脚本需要主动失败时,优先返回 `{ ok: false, failedCondition: "..." }` 或抛出错误;兼容返回 `{ pass: false }``{ success: false }`,且 `{ ok: true, pass: false }` 仍按失败处理。`failedCondition``errorMessage``message``error``reason``summary` 会进入失败摘要;不要只把失败埋在普通业务字段里。 - 自定义脚本需要主动失败时,优先返回 `{ ok: false, failedCondition: "..." }` 或抛出错误;兼容返回 `{ pass: false }``{ success: false }`,且 `{ ok: true, pass: false }` 仍按失败处理。`failedCondition``errorMessage``message``error``reason``summary` 会进入失败摘要;不要只把失败埋在普通业务字段里。
- OpenCode iframe 或 direct-host smoke 必须等到 OpenCode frame/direct origin 可用后再操作 composer;填入 prompt 后优先点击可见 `[data-action='prompt-submit']`,不要把 Enter-only 当成稳定提交路径。关闭 OpenCode 对话类 issue 时,`web-probe script` 顶层 `ok` 不能只是“脚本没抛错”;必须断言 DOM body 出现最终 assistant 文本、没有残留 `Thinking`,并记录 EventSource 或等价事件探针看到 `message.part.updated``step-finish``session.idle`。手工 EventSource 探针可以证明 `/global/event` 消息到达,但不能替代 UI DOM 终态证据 - OpenCode iframe 或 direct-host smoke 优先使用 typed `web-probe opencode-smoke --node <node> --lane <lane>`,不要重复手写一次性 Playwright 片段。该命令负责登录 HWLAB public origin、打开 `/opencode`、定位 iframe/direct origin、打开 project/composer、点击可见 `[data-action='prompt-submit']`、采集 EventSource,并断言 DOM assistant 文本与 update/finish/idle 终态。只有 typed 命令覆盖不到的额外 API 或 UI 细节才退回 `web-probe script`
- 自定义 OpenCode smoke 必须等到 OpenCode frame/direct origin 可用后再操作 composer;填入 prompt 后优先点击可见的 `[data-action='prompt-submit']`,不要把 Enter-only 当成稳定提交路径。关闭 OpenCode 对话类 issue 时,`web-probe script` 顶层 `ok` 不能只是“脚本没抛错”;必须断言 DOM body 出现最终 assistant 文本、没有残留 `Thinking`,并记录 EventSource 或等价事件探针看到 `message.part.updated``step-finish``session.idle`。手工 EventSource 探针可以证明 `/global/event` 消息到达,但不能替代 UI DOM 终态证据。
- web-probe 由 UniDesk CLI 从 YAML 声明的 bootstrap admin sourceRef 读取凭据并建立同源 `hwlab_session`;脚本不得自行读取、打印或复制 Web 登录凭据、cookie、token 或完整 API key。 - web-probe 由 UniDesk CLI 从 YAML 声明的 bootstrap admin sourceRef 读取凭据并建立同源 `hwlab_session`;脚本不得自行读取、打印或复制 Web 登录凭据、cookie、token 或完整 API key。
- 需要禁用 `EventSource`、mock `Date`/clock、注入 preload hook 或修改浏览器启动前全局对象时,`page.addInitScript()` 必须在目标页面整页加载前注册,并用 `page.goto(new URL(path, origin).toString(), { waitUntil: "domcontentloaded" })` 进入目标 deep link;不要依赖 `gotoStable()` 复用当前 SPA 页面后再期待 init script 生效。若只在已加载页面上 route/block 请求,既有 SSE 或 store 状态可能继续更新,不能作为“无后端刷新依赖”的验收。 - 需要禁用 `EventSource`、mock `Date`/clock、注入 preload hook 或修改浏览器启动前全局对象时,`page.addInitScript()` 必须在目标页面整页加载前注册,并用 `page.goto(new URL(path, origin).toString(), { waitUntil: "domcontentloaded" })` 进入目标 deep link;不要依赖 `gotoStable()` 复用当前 SPA 页面后再期待 init script 生效。若只在已加载页面上 route/block 请求,既有 SSE 或 store 状态可能继续更新,不能作为“无后端刷新依赖”的验收。
- 脚本构造 URL 时使用 `new URL(path, baseUrl).toString()`;不要拼出 `//v1/...` - 脚本构造 URL 时使用 `new URL(path, baseUrl).toString()`;不要拼出 `//v1/...`
+2 -2
View File
@@ -117,9 +117,9 @@ Workbench prompt、TraceTimeline、final response、详情弹窗、session 切
HWLAB 接入 OpenCode 时,默认采用独立 `opencode-server` Pod 和独立 public hostname,通过 Cloud Web 的 `/opencode` 导航与 iframe 集成;不要把 OpenCode UI 与 HWLAB Cloud Web 合并到同一个 Pod,也不要让 public OpenCode hostname 直接暴露 OpenCode Basic Auth。Cloud Web 继续拥有 HWLAB 登录态和同源 sessionOpenCode public hostname 应先经过 Cloud Web 鉴权代理;未登录访问 OpenCode host 的健康或 API 路径时,预期是 Cloud Web 的鉴权失败响应,而不是浏览器 Basic Auth 弹窗。 HWLAB 接入 OpenCode 时,默认采用独立 `opencode-server` Pod 和独立 public hostname,通过 Cloud Web 的 `/opencode` 导航与 iframe 集成;不要把 OpenCode UI 与 HWLAB Cloud Web 合并到同一个 Pod,也不要让 public OpenCode hostname 直接暴露 OpenCode Basic Auth。Cloud Web 继续拥有 HWLAB 登录态和同源 sessionOpenCode public hostname 应先经过 Cloud Web 鉴权代理;未登录访问 OpenCode host 的健康或 API 路径时,预期是 Cloud Web 的鉴权失败响应,而不是浏览器 Basic Auth 弹窗。
OpenCode provider/model、public hostname、SecretRef 和 extra FRP proxy 都必须从 issue/CLI 选中的 node/lane YAML 与目标 HWLAB repo render 读取;长期参考不硬编码当前模型或端口。关闭 OpenCode 集成、provider profile 或微前端入口类 issue 时,最小证据应来自选中 node/lane 的 `web-probe script`:登录 HWLAB public origin,打开 `/opencode`确认 iframe origin、OpenCode `/global/health``/config` 中的 provider/model、`/session` 创建,以及 `/session/:id/message` 返回的 providerID、modelID、terminal finish 和最终 assistant text。只看到 `opencode-server` Pod ready、Caddy hostname 200 或 Secret 存在,不能替代这个浏览器入口 smoke。 OpenCode provider/model、public hostname、SecretRef 和 extra FRP proxy 都必须从 issue/CLI 选中的 node/lane YAML 与目标 HWLAB repo render 读取;长期参考不硬编码当前模型或端口。关闭 OpenCode 集成、provider profile 或微前端入口类 issue 时,最小浏览器证据优先使用选中 node/lane 的 `web-probe opencode-smoke`:登录 HWLAB public origin,打开 `/opencode`定位 iframe/direct OpenCode origin,打开 project/composer,点击可见 submit,确认最终 assistant 文本和 EventSource 终态事件。只有需要额外核对 `/global/health``/config` 或底层 `/session/:id/message` payload 时,才补 `web-probe script` 做 bounded API drill-down。只看到 `opencode-server` Pod ready、Caddy hostname 200 或 Secret 存在,不能替代浏览器入口 smoke。
排查 OpenCode 对话长时间停在 `Thinking` 或无 assistant 文本时,先拆分 provider 完成、Cloud Web 代理长连接和 UI live state 三层事实。provider 侧必须按 `opencode-provider-proxy` service 明确查 OTel,确认 `/v1/chat/completions` 状态、耗时、content chunk、reasoning-only drop 和 done lineCloud Web 侧必须能看到 `/global/event``opencode.proxy.stream.start` span,以及 `opencode.proxy.sse.directory_rewrite_enabled``from=/workspace``to=/``ticket_accepted=true` 等属性。关闭证据必须包含浏览器 DOM 最终 assistant 文本、没有残留 `Thinking`,以及 EventSource 收到 `message.part.updated``step-finish``session.idle` 等事件;provider 200 或 `/session/:id/message` 返回 terminal 不能单独证明 UI 已收敛。OpenCode composer smoke 应优先点击可见的 `[data-action='prompt-submit']` 提交按钮;只依赖 Enter 在 contenteditable 状态下不稳定。 排查 OpenCode 对话长时间停在 `Thinking` 或无 assistant 文本时,先拆分 provider 完成、Cloud Web 代理长连接和 UI live state 三层事实。provider 侧必须按 `opencode-provider-proxy` service 明确查 OTel,确认 `/v1/chat/completions` 状态、耗时、content chunk、reasoning-only drop 和 done lineCloud Web 侧必须能看到 `/global/event``opencode.proxy.stream.start` span,以及 `opencode.proxy.sse.directory_rewrite_enabled``from=/workspace``to=/``ticket_accepted=true` 等属性。关闭证据必须包含 `web-probe opencode-smoke` 或等价浏览器 DOM 最终 assistant 文本、没有残留 `Thinking`,以及 EventSource 收到 `message.part.updated``step-finish``session.idle` 等事件;provider 200 或 `/session/:id/message` 返回 terminal 不能单独证明 UI 已收敛。OpenCode composer smoke 应优先点击可见的 `[data-action='prompt-submit']` 提交按钮;只依赖 Enter 在 contenteditable 状态下不稳定。
## HWLAB FRP 维护 ## HWLAB FRP 维护
+6
View File
@@ -61,6 +61,12 @@ OA Event Flow 的高频 trace 统计不得把每个 `trace-stats-updated` 投影
CLI 写 stdout/stderr 遇到下游 pipe 关闭的 `EPIPE` 必须安静退出,不能打印 Bun stack trace。常见验证命令是 `set -o pipefail; bun scripts/cli.ts server status | head -1`,应只看到第一行 JSON 而无额外错误噪声。 CLI 写 stdout/stderr 遇到下游 pipe 关闭的 `EPIPE` 必须安静退出,不能打印 Bun stack trace。常见验证命令是 `set -o pipefail; bun scripts/cli.ts server status | head -1`,应只看到第一行 JSON 而无额外错误噪声。
## OpenCode Trace Search
HWLAB OpenCode 对话排障时,`platform-infra observability search --grep` 是首选入口,而不是默认手写宽泛 TraceQL。`--grep opencode.proxy.stream.start` 应推断为 span name 查询,`--grep /global/event` 应推断为 route 查询,`--grep opencode.proxy.sse.directory_rewrite_enabled=true` 应推断为属性 bool 查询;候选 trace 取回后还必须在 raw body、span name、status message、route 和 full span attributes 中做二次匹配。默认输出必须披露 `grepCoverage``grepQueryInference`,无命中时给出显式 TraceQL 与扩大 candidate/window 的下一步。
OpenCode provider 侧至少保留 `opencode.provider.sse.content_chunks``content_chars``output_data_lines``done_lines``json_errors``reasoning_only_choices_dropped` 等属性摘要;Cloud Web `/global/event` 侧至少保留 `opencode.proxy.stream.start``opencode.proxy.sse.directory_rewrite_enabled`、directory rewrite from/to、`opencode.proxy.ticket_accepted` 和 streaming 标记。OTel 只能证明链路状态,关闭 UI 卡住类问题仍必须回到 `web-probe opencode-smoke` 或等价浏览器 DOM/EventSource 终态证据。
## Task Liveness ## Task Liveness
backend-core 必须把 queued、dispatched、running 视为待处理任务,并通过 `TASK_PENDING_TIMEOUT_MS` 对长时间没有 provider 终态回报的任务做超时处理。超时任务转为 failedresult 中保留 timeout、previousStatus 和 previousResult 摘要,避免 `态势总览` 的待处理数量长期卡住且无法解释。 backend-core 必须把 queued、dispatched、running 视为待处理任务,并通过 `TASK_PENDING_TIMEOUT_MS` 对长时间没有 provider 终态回报的任务做超时处理。超时任务转为 failedresult 中保留 timeout、previousStatus 和 previousResult 摘要,避免 `态势总览` 的待处理数量长期卡住且无法解释。
-1
View File
@@ -2019,7 +2019,6 @@ export function parseNodeWebProbeOptions(args: string[]): NodeWebProbeOptions {
"Passing means the browser DOM reached assistant text and the OpenCode EventSource produced update, finish and idle evidence.", "Passing means the browser DOM reached assistant text and the OpenCode EventSource produced update, finish and idle evidence.",
], ],
generatedPreferredCommands: { generatedPreferredCommands: {
rerun: commandLabel,
withExpectedText: `${commandLabel} --expect-text <assistant-substring>`, withExpectedText: `${commandLabel} --expect-text <assistant-substring>`,
}, },
scriptSource: { scriptSource: {