xworkmate-app/docs/cases/06-gateway-turn-stability-and-robustness.md
Haitao Pan 3422aae9bf docs(cases/06): mark S1 done — default expectedArtifactDirs (live-verified, bridge 0280893)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 06:34:32 +08:00

30 KiB
Raw Blame History

Gateway Turn 稳定性与健壮性|全链路分析与编码改进规划

适用目录:xworkmate-app/docs/cases/

相关仓库(/Users/shenlan/workspaces/

  • ai-workspace-infra/Caddy 入口、OpenClaw gateway 部署、playbooks
  • ai-workspace-lab/xworkmate-app Flutter 客户端、xworkmate-bridge Go 网桥)
  • ai-workspace-service/accounts / console / litellm 等旁路服务)

触发现象:任务进度条 任务运行中... 永不结束、停止 无效,实际 OpenClaw gateway 已执行完毕 伴随报错 Bridge 响应读取中断本轮结果未完成。错误码ACP_HTTP_CONNECTION_CLOSED

结论速览2026-06-26 live 验证后)

  • 「采集AI资讯无法产出」的主根因OpenClaw 网关启动时未加载 openclaw-multi-session-plugins 插件(插件文件在临时路径,网关启动早于文件就位),导致 xworkmate.* 网关方法全部 unknown method。重启网关即恢复(详见 §4「2026-06-26 决定性根因」)。
  • 本文 §3/§5 的 T1T9健壮性加固(入口超时、客户端 deadline/停止、bridge 持久 run 仓),用于让上述故障表现为「有界可恢复」而非「无限运行/丢结果」——它们正确且应保留,但不是该故障的根因修复。
  • ⚠️ 作废:早期「xworkmate.* 协议命名空间漂移、需 bridge 改原生 tasks.get」的判断错误fix/gateway-task-protocol-alignment 分支不要合并。

1. The full chain (one gateway turn) — 四层,含 openclaw-multi-session-plugins

一次 gateway turn 跨四层App → bridge(Go) → openclaw-multi-session-plugins(TS 插件) → OpenClaw gateway runtime。 插件是关键中间层:它给「多会话/多线程」补上逻辑隔离的 artifact scope会话映射 + 任务快照,并以 xworkmate.* 网关方法暴露给 bridge。

App.executeTask ──SSE POST /acp/rpc──▶ Bridge.handleRequest
   │                                      │
   │  ① xworkmate.session.prepare ───────▶ Gateway ──▶ [plugin] recordXWorkmateSessionMapping
   │       (建 appThreadKey⇄openclawSessionKey 映射 + prepareXWorkmateArtifacts)
   │       ◀── { artifactScope: tasks/<sani(sessionKey)>/<runId>, artifactDirectory, expectedArtifactDirs }
   │  ② chat.send (gateway 原生) ────────▶ Gateway ──▶ 派发 agent run返回 runId~25msdetached
   │       ◀── { runId, status: started }
   ▼
 Bridge 记 sess.openClaw + DeadlineAt(budget by taskLoadClass),返回 running 句柄
   │
   ▼  App 持久化 associationpollOpenClawTaskAssociationInternal 轮询:
 ③ xworkmate.tasks.get(runId/openclawSessionKey/artifactScope) ─▶ Gateway ─▶ [plugin] getXWorkmateTaskSnapshot
      ├─ resolveNativeTask(host task registry by runId)  ──有──▶ 回 status(running/completed/failed) + 经 export 取 artifacts
      └─ 无 native task ──▶ exportArtifactsForTaskLookup 兜底:扫 scope 目录 + expectedArtifactDirsworkspace 根 reports//artifacts/
                            ├─ 有产物 ─▶ status=unknown, evidence=artifacts_present, 带 artifacts
                            └─ 无产物 ─▶ code=no_native_task_record / task_not_found
 ④ 终态后取产物xworkmate.artifacts.export / .list / .read插件 exportXWorkmateArtifacts
      scopeRoot = workspaceRoot/tasks/<sani(sessionKey)>/<runId>;按 requiredArtifactExtensions(.md) 判 constraintSatisfied

多会话/多线程隔离的核心(插件提供)

  • artifactScopeFor(sessionKey, runId) = tasks/<sanitize(sessionKey)>/<runId>(冒号→下划线,如 agent:main:draft:e2eagent_main_draft_e2e),每个 (会话,运行) 独立目录,互不串扰(exportArtifacts.ts:126/164)。
  • recordXWorkmateSessionMappingappThreadKey ⇄ openclawSessionKey 持久成会话扩展(taskState.ts),让跨连接/重连仍能按 appThreadKey 找回 run。
  • exportXWorkmateArtifacts 严校 requestedArtifactScope === artifactScopeFor(sessionKey,runId),不匹配抛 artifactScope does not match sessionKey/runId——调用方必须传一致的 sessionKey/runId/artifactScope 三元组bridge 的 taskGetParamsWithSessionScope 负责从 session 记录补齐;外部手工探针易踩此坑)。
  • workspace 根兜底扫描agent 常把产物写到 workspace 根的 reports/artifacts/ 而非 task scope插件用 expectedArtifactDirs 回扫这些目录纳入产物(见 openclaw-gateway-e2e-regression/ROOT_CAUSE_ANALYSIS.md Fix 0

live 实测2026-06-268787插件已加载:① session.prepare 回真实 mapping ✓;② chat.send runId=turn-…438450000bridge 日志 request_timing method=chat.send);③ tasks.get 经插件返回 code=no_native_task_recordmapping 在、但 gateway 无该 run 的 native task record+ scope 目录空、无 news.md。即链路打通到插件层,但本轮 agent 未注册可查 task、也未落产物属第 4 层 agent 执行/落盘问题,见 §4「残留」与 §7

关键代码锚点:

环节 位置
App 发起 SSE turn / 轮询 xworkmate-app/.../app_controller_desktop_thread_actions.dart:644:747
Bridge session.prepare / tasks.get / cancel xworkmate-bridge/internal/acp/rpc_handler.go:96:126taskGetParamsWithSessionScope:177
Bridge↔Gateway WebSocketdial 18789 xworkmate-bridge/internal/gatewayruntime/runtime.go:372
插件注册 xworkmate.* 网关方法 openclaw-multi-session-plugins/index.ts:126/162/176/206/221session.prepare/tasks.get/artifacts.export/.list/.read
插件 artifact scope / 会话映射 / 任务快照 src/exportArtifacts.tssrc/taskState.ts:208 getXWorkmateTaskSnapshot

2. 端到端拓扑(实测自代码 + live 8787 验证)

┌─ ai-workspace-lab/xworkmate-app  (Flutter)            executeTask → SSE POST Accept: text/event-stream
▼
┌─ ai-workspace-infra  Caddy 入口  xworkmate-bridge.svc.plus   (本机直连 127.0.0.1:8787 不经 Caddy
│     /acp* : flush_interval -1 ✓  read/write_timeout 70m(对齐 bridge 60min) keepalive 5m   ← T1/T2 已修
▼
┌─ ai-workspace-lab/xworkmate-bridge  (Go)   live commit 2333c3e
│     /acp/rpc handleRequest → session.prepare / chat.send / xworkmate.tasks.get / tasks.cancel
│     gatewayruntime: WebSocket → 127.0.0.1:18789per-session 持久 run 仓(T7/T8/T9)
▼ (WS, gateway 原生 chat.send + 插件注册的 xworkmate.*)
┌─ ai-workspace-lab/openclaw-multi-session-plugins  (TS 插件, enabled)   ← 多会话/多线程 artifact 隔离层
│     index.ts registerGatewayMethod: xworkmate.session.prepare / .tasks.get / .artifacts.export/.list/.read/.collect-and-snapshot
│     会话映射(taskState) + artifactScope=tasks/<sani(sessionKey)>/<runId>(exportArtifacts) + workspace 根兜底扫描
▼ (插件运行在网关进程内)
┌─ OpenClaw gateway runtime   npm-global openclaw 2026.6.1  @ /opt/homebrew/lib/node_modules/openclaw
│     launchd ai.openclaw.gateway, WS 18789host task registry(detached task) + agent 执行(deepseek-v4-flash)
▼
   workspace ~/.openclaw/workspace/tasks/<sani(sessionKey)>/<runId>/  → 产物(.md 等)

live 验证状态2026-06-26 23:xx

  • 网关加载 6 plugins … openclaw-multi-session-plugins(重启后;详见 §4
  • /api/ping commit=2333c3esession.prepare 回真实 mappingchat.send 成功返回 runId。
  • ⚠️ xworkmate.tasks.getno_native_task_record、scope 目录空:链路通到插件,但本轮 agent 未注册可查 task / 未落产物(第 4 层执行问题)。

旁路(ai-workspace-serviceaccounts.svc.plus(登录/Tokenconsole.svc.plusopenclaw assistant routelitellm/AI-Relay-Kit/codex-relay(模型出口)、qmd(记忆)——不在主链,但 Token/出口异常以「连接中断」形态出现,用 runId 区分。

出处Caddy …/xworkmate_bridge/templates/xworkmate-bridge-site.caddy.j2;网关部署 deploy_gateway_openclaw.yml;插件 ~/.openclaw/extensions/openclaw-multi-session-plugins(注册源当前指向临时 /private/tmp/…,见 §4 加固项)。


3. 跨层失效点清单(按链路从上到下)

# 失效描述 证据
App running 轮询分支无 deadline / 无 maxAttempts;只要 tasks.get 一直回 running 就永远轮询 app_controller_desktop_thread_actions.dart:788-794(对比 :831 的 artifact-sync 分支封顶 openClawArtifactSyncLimitReachedInternal
App 进度条 phase 仅由 pending 驱动,而 pending 被该 loop 独占持有loop 不退出 = 永远 任务运行中... assistant_task_progress_bar.dart:190pending 源 = aiGatewayPendingSessionKeysInternal
App 停止abortRun → cancelAssistantTaskForSessionInternal只向 gateway 发 tasks.cancel从不本地清 pendinggateway 不可达 / run 已丢时为空操作 → 永远停不下 app_controller_desktop_thread_actions.dart:1793-1809
入口 / Bridge 超时错配 Caddy /acp* read/write_timeout 30m,但 bridge openClawAgentWaitMaxTimeout = 60min>30min 的 complex_chain_taskSSE 在入口被掐断,而 gateway 仍在跑 → ACP_HTTP_CONNECTION_CLOSED caddy.j2 read_timeout 30m vs orchestrator.go:32
入口 /acp*flush_interval -1 与长超时;/api*/ 用 Caddy 默认(短超时、无即时 flush任何走这两条的流式 / 轮询都更脆 caddy.j2
Bridge↔Gateway WS 关联绑定连接 WS 一抖,onConnLost所有在途请求立即判 SOCKET_CLOSED——包括 OpenClaw 仍在执行的那个 run重连后无任何 pending 复关联 gatewayruntime/runtime.go:802-823:935-938takePendingLocked 清空)
Bridge 无持久 run 仓 同步 SSE 路径不落 xworkmate.jobs.*,结果只活在内存 responseCh;连接一断即丢(已存在 jobs.submit/get/list 未被 gateway submit 复用) rpc_handler.go:73jobs.*vs http_handler 同步路径
Gateway 重连丢 run 态 重连到新 WS 后 tasks.getensureProductionGatewayConnected 落到新连接,查不到 terminal → 回 stale runningnot_found rpc_handler.go:143-175
Bridge gatewayRPCError 把可重试错误统一映射为 OPENCLAW_GATEWAY_SOCKET_CLOSED,但缺少"run 仍在后台、稍后可查"的语义,客户端只能当硬失败 orchestrator.go:1678-1702
运行态 ≠ 源码(主根因,见 §4 unknown method: xworkmate.* 时,根因通常是网关未加载 openclaw-multi-session-plugins 插件(这些方法由插件注册);次因是本机 launchd 的 bridge 二进制为旧构建 网关启动日志 N plugins 是否含 openclaw-multi-session-pluginsopenclaw plugins inspectcurl /api/pingcommit

4. 根因链Root cause

"实际 gateway 已执行完毕,但任务永远停不下" =

服务端把已完成的结果弄丢(⑥ + ⑦ + ⑧) bridge↔gateway 的请求关联随 WS 连接销毁、且无持久副本run 完成事件投递到已被放弃的 channel重连后无法复关联。

+ 客户端把"无限 running"固化(① + ② + ③) :轮询无截止、进度条只看 pending、停止不本地生效。

+ 触发器(④ / ⑤) :长任务在入口 30min 处必断,把链路推向上述失效(尤其 complex_chain_task budget=60min

最小可复现路径:

  1. 提交一个 budget > 30min 或网络抖动概率高的 gateway 任务;
  2. SSE 在入口30min或 WS瞬断处断开 → App 收 ACP_HTTP_CONNECTION_CLOSED
  3. OpenClaw 后台继续跑完,但 bridge 已丢失该 run 的 pending / 无持久记录;
  4. App 落入 running 轮询(或失败后仍 pendingtasks.get 拿不到 terminal
  5. 进度条永远 任务运行中...停止 不本地生效 → 卡死。

2026-06-26 本机联合调试结论

本次现场环境:

  • AppVersion 1.1.4,应用构建 commit fb7e0ac
  • 本地 Bridgehttp://127.0.0.1:8787launchd 服务 plus.svc.xworkspace.bridge
  • OpenClaw gateway 控制台:http://127.0.0.1:18789/channels

实际复现:

curl -sS -X POST http://127.0.0.1:8787/acp/rpc \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer $BRIDGE_AUTH_TOKEN' \
  --data '{"jsonrpc":"2.0","id":"probe","method":"xworkmate.session.prepare","params":{"openclawSessionKey":"probe-session","runId":"probe-run","workspaceDir":"/tmp/xworkmate-probe","gatewayProviderId":"openclaw"}}'

修复前返回:

{"error":{"code":-32601,"message":"unknown method: xworkmate.session.prepare"},"ok":false}

结论:这不是 OpenClaw gateway 控制台 18789 的页面问题,也不是 App 展示误判;/acp/rpcacp.capabilities 正常而 xworkmate.session.prepare 不认识,说明本地 Bridge 运行态没有加载到包含 handleSessionPrepare 的新二进制。源码中 rpc_handler.goorchestrator.go 已有 fallback/usr/local/bin/xworkmate-go-core 仍是旧/无元信息构建。

现场处理:

  1. xworkmate-bridge 当前源码重建 xworkmate-go-core
  2. 备份并替换 /usr/local/bin/xworkmate-go-core
  3. macOS 上移除 com.apple.provenance / quarantine 并 codesign --force --sign -,否则 launchd 会以 OS_REASON_CODESIGNING 拒绝启动。
  4. 重载 plus.svc.xworkspace.bridge

修复后探针:

{
  "ok": true,
  "payload": {
    "fallback": true,
    "compatibilityMode": "local-session-prepare",
    "artifactScope": "tasks/probe-session/probe-run",
    "artifactDirectory": "/tmp/xworkmate-probe/tasks/probe-session/probe-run"
  }
}

回归要求:每次本地替换 Bridge 后,先用 /api/ping 确认 commit,再用上面的 xworkmate.session.prepare 探针确认返回 ok:true;不能只看 App 设置页的 Status: ok

2026-06-26 决定性根因(已 live 验证OpenClaw gateway 未加载 openclaw-multi-session-plugins 插件

四层调用链(与 openclaw-gateway-e2e-regression/ROOT_CAUSE_ANALYSIS.md 一致): App → xworkmate-bridge(Go) → openclaw-multi-session-plugins(TS 插件) → OpenClaw gateway runtime(127.0.0.1:18789)

xworkmate.* 系列网关方法不是「虚构/漂移」的——它们由 openclaw-multi-session-plugins 插件在运行时注册 index.tsapi.registerGatewayMethod("xworkmate.session.prepare" | "xworkmate.tasks.get" | "xworkmate.artifacts.export" | ".list" | ".read" | ".collect-and-snapshot")。所以 bridge 发 xworkmate.tasks.get正确契约,前提是该插件被网关加载。

现场实际故障:运行中的网关没有加载这个插件,于是所有 xworkmate.* 都回 unknown method。证据链:

  • 网关启动日志:2026-06-23 前每次都是 listening (6 plugins: … openclaw-multi-session-plugins)2026-06-26 09:21:14 那次只有 5 plugins,缺了 multi-session
  • 插件注册的源路径是临时目录 /private/tmp/openclaw-multi-session-plugins/dist/index.js;该文件直到 18:40 才被填充——晚于网关 09:21 启动约 9 小时。启动时路径不存在 → 插件未加载。
  • openclaw plugins inspect 警告:loaded without install/load-path provenance; treat as untracked local code(无 install 记录的本地代码)。

修复(已 live 验证通过)launchctl kickstart -k gui/$UID/ai.openclaw.gateway 重启网关(此时插件文件已就位)→ 日志变 listening (6 plugins: … openclaw-multi-session-plugins)xworkmate.* 方法的报错从 unknown method 变为插件内部参数校验xworkmate.session.prepare → appThreadKey requiredxworkmate.tasks.get → artifactScope does not match sessionKey/runId)——证明方法已注册、插件正在处理请求。

⚠️ 更正此前一版本文档曾判为「bridge↔gateway xworkmate.* 协议命名空间漂移、需在 bridge 侧改原生 tasks.get/artifacts.*」——该结论错误,源于当时未发现 openclaw-multi-session-plugins 插件提供这些方法。对应的 fix/gateway-task-protocol-alignment 分支native 重命名)作废、不得合并bridge 原有 xworkmate.* 协议是对的。

残留与加固项

  • 插件安装路径必须稳定:当前注册在 /private/tmp/…(重启 / tmp 清理即丢,正是本次故障诱因)。应用 openclaw plugins install <stable-path>~/.openclaw/extensions/openclaw-multi-session-plugins/ 或仓库路径重装,落正式 install 记录,避免再次「启动早于插件就位」。
  • 会话面:现场 console 的 …:dashboard:bcde1b0f… 与提交的 …:draft:… 不同面task 建在 draftrequesterSessionKeydashboard 仅是 console 自带会话视图,非断点。
  • 手工探针注意:直接构造 tasks.getsessionId 不要预带 agent:main: 前缀——bridge 会再加一层导致 agent:main:agent:main:… 双前缀,触发插件的 artifactScope does not match sessionKey/runId。app 正常路径传 draft:<id>bridge 补一层。

5. 编码改进规划Stability / Robustness TODO

排序原则:先当天可上、零协议变更的止血项L0 配置 + L2 客户端),再治本的服务端持久化L1最后横切可观测性L3。 每项标注:所属仓库 · 改动面 · 验收要点。

L0 入口配置ai-workspace-infra · 改配置即可 · 当天可上)

  • T1 对齐入口与 bridge 的超时上限 caddy.j2 /acp*read_timeout / write_timeout 提到 ≥ bridge openClawAgentWaitMaxTimeout60min+ 余量。 最好让入口超时与 orchestrator.go 的上限由同一变量 / 同源常量渲染,避免再次漂移。 验收budget=60min 的 complex_chain_task 全程 SSE 不被入口掐断。

  • T2 收敛 / 补齐非 /acp 路由的流式配置/api*(及任何承载 tasks.get 轮询、流式响应的路由)补 flush_interval -1 + 同样长超时;或显式把所有 gateway 流量收敛到 /acp*。 验收:轮询 / 流式不再依赖 Caddy 默认短超时。

L2 App 客户端止血ai-workspace-lab/xworkmate-app · 纯客户端 · 零协议变更)

  • T3 running 轮询加硬截止pollOpenClawTaskAssociationInternal 的 running-handle 分支(thread_actions.dart:788)引入 deadline / maxAttempts复用 bridge 下发的 deadlineAtopenclaw_async_tasks.go:92+ grace到点落 interrupted(可恢复态,isRecoverableAssistantTaskStateInternal 已支持),退出 loop。 参照同函数 :831 artifact-sync 分支的封顶写法 openClawArtifactSyncLimitReachedInternal。 验收gateway 始终回 running 时,客户端在 deadline 后必终止,不再无限轮询。

  • T4 停止 本地权威化 abortRun / cancelAssistantTaskForSessionInternal:1793)改为:乐观清 aiGatewayPendingSessionKeysInternal、置 lifecycle=aborted、退出 loop best-effort 发 tasks.cancel。UI 终止不得依赖 gateway 往返。 验收gateway 不可达时点 停止 仍能立刻停下。

  • T5 传输中断降级为"后台续跑·重连中" 收到实时 ACP_HTTP_CONNECTION_CLOSED 时,不直接当硬失败把任务留在 running而是降级为"已转后台 / 重连中",触发 resumeOpenClawTaskAssociationsInternal:906,目前仅在 thread 加载时触发)续轮询。 验收SSE 瞬断后,任务能自动恢复轮询并最终拿到终态或明确终止。

  • T6 失败路径与 pending 清理一致性 审计 applyGatewayChatFailureInternal:1613,置 ready 但不清 pending与调用方 finally:715!handedOffToBridgeTask 才清 pending之间的竞态确保任一终态路径都能确定性地清 pending杜绝"错误已渲染但仍 running"。 验收:失败 / 中断 / 取消三类终态后,pending 必为 false。

L1 Bridge 持久化ai-workspace-lab/xworkmate-bridge · 根因修复 · 有协议/状态面改动)

T7/T8/T9 已实现(本地验证 commit 2333c3e,需确保运行态 /api/ping.commit 与发布 commit 对齐)。 实现取舍:复用已存在的 per-session 持久 stores.sessions[sessionID] 内的 task/openClaw/lastResult,生命周期独立于 bridge↔gateway WebSockettasks.get 从「强依赖 gateway 应答」改造为「优先用持久 run 仓兜底」。 新增 internal/acp/openclaw_run_registry.go+ _test.go),改动 rpc_handler.go: handleTaskGetorchestrator.go: startOpenClawGatewayTask

  • T7 run 关联与 WS 连接解耦openClawTaskGetGatewayUnconfirmedFallbackopenclaw_run_registry.go gateway 无法确认unavailable / socket closed / not_found但 run 仍在预算内时,按 runId 从持久 session store 合成 running 句柄让客户端继续轮询,跨越瞬时抖动。 取舍:未直接改 gatewayruntime.onConnLostruntime.go:802)的 pending 判死逻辑——在途请求被判死后会以 gateway error 冒泡到 handleTaskGet,新兜底按 runId 续轮询到 deadline 已等价覆盖,且风险远低于重写连接层 pending 关联。chat.send 初次提交若 WS 中断则尚无 runId、无可复关联客户端重发即可。 验收WS 瞬断 + 重连后,已完成的 run 仍能被 tasks.get 查到 terminal + artifactsTestGatewayUnconfirmedFallbackWithinBudgetKeepsPolling)。

  • T8 终态结果落持久 run 仓cacheOpenClawTaskGetResultIfTerminal / cachedTerminalForRunLocked gateway 确认终态后,把最终客户端形态(已 decorate 下载 URL + strip 内联内容)缓存进 sess.lastResult后续轮询直接回放gateway 之后查不到也不丢。带 runId 校验 + 新 turn 复用 session 时重置 ProgressTerminal,防旧 run 终态错配新 run。 取舍:未新建独立 xworkmate.jobs.* 落库;现阶段复用 per-session 内存 store已满足「跨 WS 抖动不丢结果」。bridge 进程重启后仍会丢——若需跨重启持久化,再起 T8b 接 jobs.* / 磁盘。 验收:连接抖动后结果与 artifacts 仍可检索(见 TestTerminalResultCachedAndServedAfterGatewayLossTestCachedTerminalNotServedForDifferentRunId)。

  • T9 服务端 DeadlineAt 兜底终态markOpenClawRunDeadlineInterruptedLocked run 过期(sess.task.DeadlineAt)且 gateway 无法确认时bridge 主动回 terminal interruptedOPENCLAW_RUN_DEADLINE_EXCEEDED),与 T3 客户端 deadline 形成双保险。 取舍:仅在 gateway 无法确认时按 deadline 强制终态gateway 明确回 running强杀,避免误伤合法长任务(那一侧由客户端 T3 兜底)。 验收gateway 失联超过 budget 后,tasks.get 返回确定 terminalTestGatewayUnconfirmedFallbackPastDeadlineInterrupts)。

  • T10 错误语义细化 gatewayRPCErrororchestrator.go:1678)区分"连接断但 run 仍在后台可查" vs "run 确实失败",前者携带 runId + retryable/poll 提示,供客户端走 T5 续轮询而非硬失败。 验收:客户端能据错误语义区分"重连续跑"与"真失败"。

  • T13 运行态同步校验bridge 二进制 + 网关插件) 「源码已修但跑的不是它」是反复踩的坑,需双侧确认:

    • Bridge/api/ping.commit 非空且等于目标 commit本机 launchd 可能仍跑旧 /usr/local/bin/xworkmate-go-core)。
    • 网关插件:网关启动日志含 … openclaw-multi-session-plugins,且 /acp/rpc xworkmate.session.prepare 返回 unknown method(插件已加载时返回真实 mapping未加载时 bridge 才走 compatibilityMode=local-session-prepare 降级)。 验收App 不再显示 unknown method: xworkmate.*;网关 N plugins 列表含 multi-session。

L3 可观测性(横切 · infra/service/lab

  • T11 端到端贯穿 runId App 日志 → Caddy access log → bridge SSE 日志(已有 component=acp_ssehttp_handler.go:221)→ gateway run全链路带同一 runId,便于定位"入口断"还是"WS 断"。 验收:任一 runId 可在四层日志串联。

  • T12 关键指标 + 告警 bridge 暴露:SOCKET_CLOSED 在途任务数、gateway WS 重连计数、running 轮询超 deadline 计数。 验收:⑥ 类事件发生即在监控可见,无需靠用户截图。


6. 落地顺序建议

  1. 主根因修复live 验证):让 OpenClaw 网关稳定加载 openclaw-multi-session-plugins——openclaw plugins install 从稳定路径重装 + 重启网关,确认启动日志 6 plugins … openclaw-multi-session-pluginsxworkmate.* 不再 unknown method。这是「采集AI资讯能产出」的前提详见 §4
  2. 当天止血(已合并 mainT1 + T2入口配置+ T3 + T4 + T6客户端+ session.prepare 数字 code 降级,消除"30min 必断 / 路由漏配 / 无限 running / 停不掉"。 说明session.prepare 数字 code 降级仍有价值——当插件加载时,让 bridge 优雅 fallback 而非硬失败;插件加载后走真实 plugin 路径。
  3. 健壮性加固(本地验证 commit 2333c3eT7 + T8 + T9bridge 持久 run 仓与 WS 解耦),把网关短暂不可达 / 抖动收敛为「有界续轮询 → deadline 终态」,而非无限运行/丢结果。
  4. 跟进(待办)T5 + T10断连续跑语义、T11 + T12可观测性、T8b跨进程重启持久化xworkmate.jobs.* / 磁盘);运行态校验:每次替换 bridge 二进制 / 网关重启后,核对 /api/ping.commit 与网关 N plugins 列表。

回归对照:本目录 00-review-env-and-matrix.md 第 2 节"通用验收标准"中"长任务执行期间状态流 / 取消 / 重试稳定""同一任务重复执行 3 次不卡死",即本规划的回归出口。 产物交付链artifact scope / workspace 路径)的独立缺陷与修复,见 openclaw-gateway-e2e-regression/ROOT_CAUSE_ANALYSIS.md


7. 全链路稳定性改进(基于 2026-06-26 四层 live 验证)

优先级按「直接决定一次任务能否产出」排序。S1/S2 是本轮 live 新发现。

  • S0 插件稳定安装(最高)— 已落实并验证2026-06-27 网关方法 xworkmate.* 全部依赖 openclaw-multi-session-plugins精确根因~/.openclaw/extensions/openclaw-multi-session-plugins 是个符号链接 → /tmp/openclaw-multi-session-plugins(临时盘,重启/清 tmp 即失效);openclaw plugins inspectSource: /private/tmp/… 且警告「loaded without install/load-path provenanceuntracked local code」。网关 09:21 启动早于该 tmp 路径就位 → 当次只加载 5 plugins、xworkmate.*unknown method已执行:① 删符号链接、把内容复制成真实目录 ~/.openclaw/extensions/openclaw-multi-session-plugins;② openclaw plugins install <该路径> --force 落正式 path install 记录;③ launchctl kickstart -k gui/$UID/ai.openclaw.gateway 重启。 验证:启动日志 http server listening (6 plugins: … openclaw-multi-session-plugins)inspectSource 变为 ~/.openclaw/extensions/…/dist/index.js、provenance 警告消失;xworkmate.session.prepare 经 bridge 返回真实插件响应fallback=null、带 mappingartifactScope=tasks/draft_s0verify/s0-run),不再走 bridge 的 local-session-prepare 降级。 收尾:~/.openclaw/extensions/ 现为真实目录(非 /tmp 软链),重启/重启后不再丢插件;建议把它纳入部署(deploy_gateway_openclaw)从仓库 openclaw-multi-session-plugins 安装,避免再被软链到临时盘。

  • S1 expectedArtifactDirs 为空导致根目录兜底失效 — 已修复并 live 验证commit 0280893 根因live 的 session mapping 为 expectedArtifactDirs:[]而插件对「agent 把产物写到 workspace 根 reports//artifacts/ 而非 task scope」的兜底扫描依赖 expectedArtifactDirs;为空 → 兜底形同虚设 → 即便 agent 产出也收不到,表现「暂无文件」。 修复:orchestrator.go openClawArtifactContractForParams 在「任务期望产物(requiresExport 或推断出 requiredExts)但未声明目录」时补缺省 ["reports/","artifacts/","exports/"] 并置 requiresExport=true;纯聊天不受影响(defaultOpenClawExpectedArtifactDirs,含单测 orchestrator_s1_artifact_dirs_test.go)。 验证提交「采集AI资讯保存md」→ requiresArtifactExport=trueexpectedArtifactDirs=['reports/','artifacts/','exports/'](修复前为 [])。

  • S2 no_native_task_record 状态歧义xworkmate.tasks.get 的真值来自「gateway host task registry 有该 run 的 detached task」「artifact 已存在」。live 中 chat.send 成功但 gateway 无 native task recordagent 可能以 inline chat 执行、未注册可查 task且无产物 → 插件回 no_native_task_recordbridge 只能靠 T7 兜底续轮询到 deadline无法区分「还在跑」与「跑完没产物」。 改进:①确认 gateway 侧 chat.send 是否应产出 detached taskagent 配置/ tasks.* 注册);②插件/bridge 在 no_native_task_record 且超过最小执行时长时,下发更明确的 running(no-record) vs completed(no-artifact) 语义,配合 §5 T9 deadline 收口。 验收agent 正常执行时 tasks.get 能返回真实 running→completed异常时给确定终态而非无限 degraded。

  • S3 三元组一致性(已知约束) — 插件严校 sessionKey/runId/artifactScope 三者一致(exportArtifacts.ts:126),且 bridge 的 openclawSessionKey 由 agent:main: + appThreadKey 组成。调用方/探针不要预带 agent:main: 前缀(否则双前缀 → artifactScope does not match。bridge taskGetParamsWithSessionScope 已负责补齐保持其为唯一可信来源App/探针只传 sessionId=draft:<id> + runId

  • S4 运行态可观测 — 沿用 §5 T11/T12bridge /api/ping.commit、网关 N plugins 列表、openclaw plugins inspect 三处纳入健康检查;runId 贯穿 App→bridge→插件→gateway 日志,便于定位断点落在四层中的哪一层。