docs: update workbench web probe endpoints

This commit is contained in:
Codex
2026-06-19 03:59:03 +00:00
parent 57bcac9858
commit dc0398ad4a
+22 -15
View File
@@ -45,19 +45,24 @@ bun scripts/cli.ts hwlab nodes web-probe run --node D601 --lane v03
```bash
bun scripts/cli.ts hwlab nodes web-probe script --node D601 --lane v03 <<'JS'
export default async ({ page, gotoStable, screenshot, fetchJson, fetchApiMatrix, recordStep, safeEvaluate }) => {
await page.route('**/v1/agent/conversations**', route => setTimeout(() => route.continue(), 1000));
export default async ({ gotoStable, screenshot, fetchJson, fetchApiMatrix, recordStep }) => {
await gotoStable('/workbench');
const workspaceResponse = await fetchJson('/v1/workbench/workspace?projectId=prj_hwpod_workbench');
const sessionsResponse = await fetchJson('/v1/workbench/sessions?limit=5');
const firstSession = sessionsResponse.body?.sessions?.[0] ?? sessionsResponse.body?.items?.[0] ?? null;
const sessionId = firstSession?.sessionId ?? firstSession?.id ?? null;
const apiMatrix = await fetchApiMatrix([
'/v1/workbench/workspace?projectId=prj_hwpod_workbench',
'/v1/workbench/sessions?limit=5',
...(sessionId ? [
`/v1/workbench/sessions/${encodeURIComponent(sessionId)}`,
`/v1/workbench/sessions/${encodeURIComponent(sessionId)}/messages`
] : []),
'/auth/session'
]);
recordStep('workbench-ready', { url: page.url(), apiMatrixOk: apiMatrix.ok });
recordStep('workbench-ready', { sessionsOk: sessionsResponse.ok, sessionId, apiMatrixOk: apiMatrix.ok });
return {
url: page.url(),
workspaceOk: workspaceResponse.ok,
workspace: workspaceResponse.body,
sessionsOk: sessionsResponse.ok,
sessionCount: sessionsResponse.body?.sessions?.length ?? sessionsResponse.body?.items?.length ?? null,
sessionId,
screenshot: await screenshot('workbench.png')
};
};
@@ -71,7 +76,9 @@ JS
- web-probe 由 UniDesk CLI 从 YAML 声明的 bootstrap admin sourceRef 读取凭据并建立同源 `hwlab_session`;脚本不得自行读取、打印或复制 Web 登录凭据、cookie、token 或完整 API key。
- 脚本构造 URL 时使用 `new URL(path, baseUrl).toString()`;不要拼出 `//v1/...`
- 先通过 `gotoStable``waitWorkbenchReady` 或等价导航进入目标 origin,再用 `fetchJson` 读同源 API;不要在 `about:blank` 上请求 `/v1/...``fetchJson` 返回包装对象 `{ ok, status, body, failureKind, ... }`,业务字段必须从 `.body` 读取,并同时记录 `ok/status/failureKind`;如果返回 `status=0` 且页面还不是目标 origin,先按 probe 脚本误用处理,不要归因成 Cloud API 或 Workbench 投影失败。
- Workbench 有 SSE/长轮询时不要用 `networkidle` 判断通过;用明确 DOM/API 条件,如 final URL、route conversationId、active tab、message card、trace row 或 workspace `selectedConversationId`
- Workbench 有 SSE/长轮询时不要用 `networkidle` 判断通过;用明确 DOM/API 条件,如 final URL、route sessionId、active tab、message card、trace row 或 durable session detail/messages
- 当前 HWLAB v0.3 Workbench durable read model 的验收端点是 `/v1/workbench/sessions``/v1/workbench/sessions/{sessionId}``/v1/workbench/sessions/{sessionId}/messages``/v1/workbench/turns/{traceId}``/v1/workbench/traces/{traceId}/events`;提交 mutation 可用正式 `/v1/agent/chat` 产生真实 turn。`/v1/workbench/workspace` 只在当前 API 契约声明为选择/config 快照时采集,不作为消息或 trace authority`/v1/agent/conversations` 属于旧模型,不得作为 v0.3 Workbench durable projection 的通过条件。
-`web-probe run --fresh-session --message ...` 在当前 v0.3 上报 `session-not-selected`,且证据显示 helper 仍探测 `/v1/workbench/workspace``/v1/agent/conversations` 并得到 404,不要把它直接归因为投影超时或 Cloud Web 回归;改用 `web-probe script` 按当前 durable 端点提交/轮询/截图,并按 `$post-task` 去重登记工具链反馈。
- Playwright `page.evaluate` 只能传一个可序列化参数;多个值包成对象,或用 `safeEvaluate(fn, { a, b })`
- `safeEvaluate` 返回结构化包装对象;脚本断言前必须解包,例如 `const evaluated = await safeEvaluate(...); const dom = evaluated?.value ?? evaluated;``recordStep` 可以记录包装对象用于诊断,但行为断言不要直接读包装对象的业务字段,避免把已通过的 DOM 误判成 `undefined`
- 脚本用 `page.evaluate` 模拟滚动、resize、click 或派发 DOM event 后,若断言 Vue/React 绑定出的 `data-*`、ARIA、按钮状态或 scroll-follow 状态,必须先等待一次浏览器/框架更新,例如 `await wait(300)``page.waitForFunction(...)` 或下一次明确 DOM 条件;不要在同一个 evaluate 内写完事件就立刻读取状态,否则会把尚未 flush 的响应式状态误判为业务失败。验证用户滚动暂停/恢复时优先记录操作前后 scroll metrics 和 `data-following` 的延迟稳定值。
@@ -93,7 +100,7 @@ bun run e2e:workbench -- --project=chromium
运行面要求:
- 在 issue/CLI 选中的 node/lane workspace 或其独立 worktree 上运行,不在 master server 本地运行。
- 同源 fake-server 只提供正式 API 契约,覆盖 `/auth/*`workspace、conversation list/detail、turn snapshot、trace event page、Workbench event stream、Performance summary 等当前用例所需端点。
- 同源 fake-server 只提供正式 API 契约,覆盖 `/auth/*`session list/detail/messages、turn snapshot、trace event page、Workbench event stream、Performance summary 等当前用例所需端点。
- 测试断言优先使用用户可见文本、ARIA role 和稳定 DOM 标识;不要通过内部 store、localStorage、数据库或 live API 直接判定通过。
- 非终态瞬时不变量必须用立即 DOM 快照断言:例如 running/pending 阶段要求 final response 为空时,先定位当前 agent card,再用 `locator.evaluate` / `page.evaluate` 直接读取 `.message-markdown.message-text` 数组并立刻判断;不要用会自动等待的 `expect(locator).toHaveCount(0)``not.toContainText` 或类似负向 locator 断言,否则测试可能等到 terminal DOM 替换后误通过,掩盖 running 阶段污染。
- 失败必须保留截图、Playwright trace 或等价 artifact;关键路径可生成命名截图。
@@ -110,7 +117,7 @@ Workbench 至少覆盖:
## Fixture 采集与脱敏
- 采集范围至少包括 workspace selection、conversation list、conversation detail、session/turn snapshot、trace event page、Workbench event stream 和 Web performance summary。
- 采集范围至少包括 session list/detail/messages、session/turn snapshot、trace event page、Workbench event stream 和 Web performance summaryworkspace selection/config 只有在当前 API 契约仍声明该读模型时才采集
- 产物必须记录 `capturedFrom``capturedAt`、schema/redaction 版本和 `valuesPrinted=false`
- 使用稳定伪 ID 映射保留 conversation、session、thread、turn、trace、message、sourceSeq 的关系。
- 删除或替换 `HWLAB_API_KEY`、cookie、Authorization header、DB DSN、provider token、真实用户身份、非公开 prompt、stdout/stderr 大段原文和完整 provider payload。
@@ -121,19 +128,19 @@ Workbench 至少覆盖:
- Workbench API 的 GET 路径不是修复入口。若 session/messages/turn/trace 出现状态不一致,应登记为投影唯一性或投影延迟问题,修 backend projector/read model;不得用前端轮询、session 切换、reload、read-through sync 或 GET 内部修复把问题掩盖。
- Workbench 读侧不得推理生命周期。`turn.status``message.status``session.running``trace terminal``finalResponse``projectionStatus` 只能来自唯一 durable projectiontrace event row 的 `completed` 只表示该 event/tool row 完成,不能终结 turn;message 文本为空只表示无可展示正文,不能生成占位 final responselist summary、session detail、turn snapshot、trace page 之间的差异只能作为 diagnostic,不得用优先级函数在读侧“选一个看起来合理的状态”。
- Workbench durable projection 验收必须覆盖同一 session/trace 的 list、detail、messages、turn、trace events 和 DOM session tab/message card;尤其要用 fresh browser 或 runtime 重启后的历史 terminal 样本证明 read model 能从 durable trace events 恢复终态。若内存 trace 仍显示 running 而 durable trace 已 terminal,应修唯一 read model 的投影对象,不得在 GET handler、前端 reducer 或 probe 脚本里做临时状态仲裁。
- Session rail 会话集合只消费 `/v1/agent/conversations`;当前 conversation 消息本体只消费 `/v1/agent/conversations/{conversationId}`turn 状态只消费 `/v1/agent/turns/{traceId}`trace 阅读只消费 trace event page/result snapshot。不要让 workspace snapshot、列表摘要、localStorage、trace polling 或 result polling 互相覆盖。
- Session rail 会话集合只消费 `/v1/workbench/sessions`;当前 session detail 只消费 `/v1/workbench/sessions/{sessionId}`;消息本体只消费 `/v1/workbench/sessions/{sessionId}/messages`turn 状态只消费 `/v1/workbench/turns/{traceId}`trace 阅读只消费 `/v1/workbench/traces/{traceId}/events` 或正式 trace event page。不要让 workspace snapshot、列表摘要、localStorage、trace polling 或 result polling 互相覆盖。
- 空消息 session 回收验收必须同时证明:未超过 TTL 的空 session 保留,有消息、running/admitting、terminal result 或 active trace 的 session 不被回收;超过 TTL 的空 idle session 由后端 GC 归档/删除后,rail/list/detail/messages/deep link 都按同一 canonical lifecycle 状态表现。
- UI transient state 只保存 route、显式选择、composer draft、scroll、展开状态和临时交互状态;服务端事实进入 server-state/reducer/projection。
- running turn 必须同时反映在主任务区、composer 主按钮、session tab 和可取消入口;清空、切换、刷新不得把 running trace 隐藏成空态。
- 显式新建或选择 session 是 active selection 的 mutation authority`POST /v1/agent/sessions` 返回 201 或用户点击目标 session 后,URL、active tab、当前消息区和 composer 必须立即归属目标 session。目标 session detail/messages 慢只能表现为该目标 session 的 loading/empty 态;旧 session 的 list refresh、detail、turn、trace、SSE 迟到事件只能更新旧 session 自己的 cache/status,不得继续占据或夺回 active route、message area、composer/cancel 状态。
- 读侧没有破坏性投影权。`GET /v1/workbench/sessions`、session detail、messages、SSE、route hydrate 或 refreshSessions 的失败、404、空列表、网络错误和迟到响应,只能显示 loading/degraded/unknown/blocker 或保留上一份成功投影;不得 `forgetSession()``replaceActiveSessionSelection(null)`、清空 messages/tabs,或把 composer 改成 `session_required`。deleted/archived/not-found 必须来自后端 canonical lifecycle projection,或来自用户显式 DELETE mutation 成功后的展示更新。
- terminal failure、blocked、canceled 都是最终结果;若 API 返回可读 error/blockerfinal response 展示区必须直接显示,不能只放在 trace rows 或详情面板中。
- session 切换验收必须覆盖点击态和持久化恢复态:选择目标 session、确认 workspace `selectedConversationId` 持久化、刷新后同一 session 仍 active。
- completed turn / Trace 重放 / deep link 验收必须用 fresh browser context 或等价 fresh login 直接打开 `/workbench/sessions/<conversationId>?projectId=<projectId>`,不能只用同页 reload。
- session 切换验收必须覆盖点击态和持久化恢复态:选择目标 session、确认 route/session list/detail/messages 归属同一 `sessionId`、刷新后同一 session 仍 active。
- completed turn / Trace 重放 / deep link 验收必须用 fresh browser context 或等价 fresh login 直接打开 `/workbench/sessions/<sessionId>`,不能只用同页 reload。
- 0repair 验收必须用 fresh context 直达 session/deep link,或从当前 session 显式切换到目标 session;不得依赖测试 helper 选择、reload、切走再切回、`sessionRepair``realignFreshSession` 修正页面后再判通过。旧 session 的延迟 detail、turn、trace、SSE 或 refreshSessions 晚到,只能更新该 session cache/list,不能改变 active URL、active tab、current messages 或 composer。
- Workbench reducer/selectors/0repair 收敛类 closeout 必须做负向源码扫描并在 issue/PR 证据里写明结果;至少覆盖直接写服务端事实和旧 repair 名称,例如 `messages.value =``messages.value.push``sessions.value =``previousMessages``previousSessionId``restorePrevious``restoreMessagesTraceAuthority``repairWorkbenchMessagePage``findMessageTextFallback``projectionCatchup``PROJECTION_CATCHUP` 和组件侧 `workbench.messages`。这些扫描是辅助证据,不能替代 fake-server Playwright 与 public web-probe。
- Trace 实时性要用间隔采样,区分“加载中”和“思考中”;终态卡片不得继续保留“思考中”空态。
- 用户反馈“当前 session 不刷新、切走再切回才刷新”时,证据必须成对采集:直达目标 URL、原地等待、切到其他 session、切回目标 session 的 DOM 截图;同时记录 `/v1/workbench/workspace`、conversation list with `includeConversationId`、conversation detail、turn snapshot 和 trace page 的状态。issue 正文要明确这是事件投影/重放问题,不能只登记成普通 deep link 或单接口 404。
- 用户反馈“当前 session 不刷新、切走再切回才刷新”时,证据必须成对采集:直达目标 URL、原地等待、切到其他 session、切回目标 session 的 DOM 截图;同时记录 session list、session detail、messages、turn snapshot 和 trace event page 的状态。issue 正文要明确这是事件投影/重放问题,不能只登记成普通 deep link 或单接口 404。
## Performance 页判定口径