
qwen-code 多工作区 Channel Worker 正确性加固PR 6635 Review Follow-ups 实现计划解读【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code本文基于 qwen-code 仓库中的实现计划 2026-07-10-pr-6635-review-followups.md系统讲解多工作区 daemon 托管 channel worker 场景下六项正确性补强的设计动机与源码级实现统一的 channel cwd 解析器、监听启动期的完整归属校验与计划冻结、以 worker group 为边界的生命周期回滚与重载合并、runtime 的就绪发布时序以及单工作区兼容的 pidfile / daemon-status 协议演进。读完本文你可以理解qwen serve --channel name在多工作区模式下如何保证要么全部就绪、要么干净回滚并能定位到仓库中对应的分组算法、监督组与测试文件进行查证。一、问题背景多工作区 daemon 下的 channel worker 正确性缺口在单工作区模式下qwen serve --channel name会为 primary workspace 启动一个 channel worker 进程。当 daemon 演进到多工作区模式后每个已注册、已信任的工作区都需要拥有自己独立的 worker 进程绑定该工作区的 cwd、QWEN_DAEMON_WORKSPACE与 per-workspace 环境变量叠加层Phase 4b 设计文档 描述了这一分组模型。PR 6635 引入了这组能力但代码评审暴露出若干正确性缺口解析层与所有权判定层使用不同的 cwd 解析逻辑、部分 worker 启动失败后可能留下半启动状态、并发重载可能交错执行、多工作区的状态诊断字段缺失等。该实现计划的总体目标是在不新增 workspace-qualified channel 控制 API 的前提下闭合这些正确性缺口。即不扩展 CLI 语法面只收紧既有路径的语义。计划共列出六项实现要点下面逐条结合源码展开。二、要点 1统一 cwd 解析器解析与所有权分组共用同一逻辑计划要求使用同一个 cwd resolver 用于配置解析与所有权分组普通相对路径的 channel cwd 相对于 settings 所在工作区解析同时保留绝对路径与~/家目录相对路径的既有语义。仓库中的落地是两个函数组合resolveChannelCwd 负责路径解析语义export function resolveChannelCwd( rawCwd: string | undefined, defaultCwd: string, ): string { if (!rawCwd) return resolvePath(defaultCwd); if (isHomeRelative(rawCwd)) return resolvePath(rawCwd); return path.resolve(defaultCwd, rawCwd); }即未配置cwd时回落到加载配置的工作区~/~/...按家目录解析isHomeRelative同时兼容 Windows 风格的~\前缀其余相对路径用path.resolve(defaultCwd, rawCwd)相对加载该配置的工作区解析。resolveChannelOwnerCwd 再叠加一步规范化export function resolveChannelOwnerCwd( rawCwd: string | undefined, workspaceCwd: string, ): string { return canonicalizeWorkspace(resolveChannelCwd(rawCwd, workspaceCwd)); }canonicalizeWorkspace来自 acp-bridge 的 workspacePaths 模块消除符号链接、大小写等表达差异。这样处理的关键意义在于serve 层的分组判定与 worker 进程侧自己的validateChannelWorkspaces校验使用的是同一条解析链两侧的归属结论不可能分叉——否则会出现daemon 认为 channel 属于工作区 A而 worker 启动后校验失败的隐性冲突。单测 channel-workspace-grouping.test.ts 直接覆盖了这三种语义未设cwd默认归属加载工作区、显式cwd独立于工作区被规范化、相对路径如../secondary相对加载工作区解析以及~/qwen-channel-workspace家目录相对路径命中已注册工作区。三、要点 2监听启动期解析并校验完整归属计划然后冻结计划第二条要求在暴露可用的 daemon handle 之前就在 listener 启动阶段把整个 channel→workspace 计划解析并校验完毕未知zero owners、歧义multiple owners、未信任untrusted、无法规范化canonicalization failure的归属关系都必须在此阶段拒绝随后将计划冻结供运行时创建使用。核心是纯函数 resolveChannelWorkspaceGroups。其所有权判定规则为channelname归属于工作区W当且仅当resolveChannelOwnerCwd(cfg[name].cwd, W)规范化后恰好等于W。由于loadChannelsConfig(W)返回的是合并了 system/user/workspace 三层的settings.merged.channels哪个工作区的合并配置里出现了这个 name并不能判定归属——只有 resolved cwd 回指该工作区才算数。由此导出四种确定性结论情形判定结果显式cwd指向某已注册工作区 X唯一 owner X无歧义无cwd且仅定义在某工作区自己的 settings 中唯一 owner 该工作区无cwd且定义在 user/system 层每个工作区都满足 →ambiguous_channel_workspace显式cwd指向未注册路径或任何工作区都没有该 name0 个 owner →channel_workspace_mismatch此外还有两个前置/后置校验注册表中没有 primary 工作区时返回no_primary_workspace唯一 owner 未通过信任校验时返回untrusted_workspacechannel worker 需要创建会话必须运行在受信任工作区。无法规范化如 EACCES 导致canonicalizeWorkspace抛错的 cwd 会被当作该工作区不是 owner处理若最终所有工作区都不满足则落入 0-owner 错误而不是静默跳过。--channel all在 v1 保持 primary-only避免隐式跨工作区进程扇出。该函数在 run-qwen-serve.ts 中的resolveChannelWorkspaceGroupsAtListen于 listen 回调中执行——早于buildRuntime输入是已注册工作区 loadSettings 启动时冻结的信任状态boot-frozen trust。任一错误码触发即拒绝启动此时还没有可用的 daemon handle 暴露给调用方。解析成功的分组结果随后被冻结frozen后续不再基于另一个文件系统快照重新分组运行时创建要点 3/4只消费这份固定计划。这一时序选择保证启动判定与 worker 校验始终基于同一份 settings/trust 视图。四、要点 3以 worker group 为生命周期边界——回滚、合并、等待计划第三条把worker group确立为生命周期边界包含三句话语义全部落在 ChannelWorkerGroup 的实现中1) 初始启动部分失败时回滚。start()串行启动各 supervisor而非并行每启动一个就记入started列表任一步失败后await stopEntriesBestEffort(started)尽力停掉已就绪的 worker再向外抛出原始错误try { for (const entry of entries.values()) { started.push(entry); await entry.supervisor.start(); // 期间若 stopping 或 workspace 被 drain同样视为启动失败 } groupStarted true; } catch (error) { groupStarted false; await stopEntriesBestEffort(started); throw error; }同时循环内还会检查drainingWorkspaces与stopping标志防止启动过程中并发触发的 workspace 移除/停机留下新启动但无人管理的 worker。2) daemon 级重载合并并发请求任一重启失败则整队停止。reconcile()是 daemon 级重载事务入口的if (reconciling) return reconciling;让并发请求共享同一个进行中的 Promisecoalesce。事务分两段——先停旧受影响 entry失败则restoreEntries把已停的恢复回去并抛ChannelWorkerReconcileError再串行启动新 entry失败则stopEntriesForRollback(startedNew)清理新启动的再restoreEntries(stoppedOld)恢复旧的。错误对象携带rolledBack/rollbackError/stopFailed/startupFailures字段区分已回滚回滚本身失败停止失败三种严重度。选择任一失败即整队停止的策略是为了避免留下一个只重载了一半的 fleet——半新半旧的 worker 集合比整体回滚更难诊断。3) 停机等待重载完成。stop()先置stopping true然后await reconciling?.catch(() {})等待进行中的 reconcile 事务结束再stopAllEntries停止全部 supervisor。这样 shutdown 不会与半完成的重载事务交错。信号处理器的紧急兜底由killAllSync()提供它会同时遍历正式 entry 与pendingEntriesreconcile 期间尚未提交的新 entry保证任何时机都不会漏杀 worker。围绕 group 的快照与路由同样按工作区维度化snapshots()返回每个 worker 的ChannelWorkerGroupSnapshot含workspaceId/workspaceCwd/primaryprimarySnapshot()专门支撑 legacy 单 worker 字段enqueueWebhookTask/deliverChannelMessage通过routeEntry把任务路由到该 channel 的拥有 supervisor找不到 owner 时返回结构化的channel_worker_unavailable错误该 webhook 路由能力在 PR #6635 剩余评审意见计划 的 Task 3 中补回。group 还实现了beginWorkspaceDrain/cancelWorkspaceDrain/removeWorkspace/restoreWorkspace与 daemon 的 workspace 移除/恢复流程联动。五、要点 4全部 worker 就绪后才发布新构建的 runtime app计划第四条新构建的 runtime app 只在所有 worker 就绪后才发布启动失败时保持 health 为 degraded。从 run-qwen-serve.ts 的结构看completeRuntimeStartup是所有 runtime 启动路径deps.bridge快路径与startRuntime → buildRuntime路径的唯一汇聚点多工作区模式由于deps.bridge仅限单工作区必然走startRuntime → buildRuntime。在这个汇聚点里新 runtime app 先发布并挂接到 ACP 传输worker 引导时需要 runtime 的/capabilities路由且连接后可能立刻收到 channel 流量因此 daemon 会话路由必须先可用随后创建并启动各 worker supervisorruntimeReadyPromise 直到所有被请求的 supervisor 到达 ready 才 resolve调用方如等待 handle 的 CLI/SDK 客户端先 awaitruntimeReady再使用 handle。失败路径上channel worker 启动失败仍是致命错误runtime 发布先撤回再拆除 group、pidfile、bridges 与 listener不会留下一个在监听但功能不完整的 daemonhealth 报告保持 degraded 状态。Phase 4b 设计文档 中Orchestration and timing一节进一步说明group 的取消cancellation还会阻止 teardown 开始之后再启动下一个 workspace 的 supervisor堵住拆除已经开始却又有新 worker 冒出来的竞态窗口。pidfile 的写入也按此设计收敛任一 supervisor 的onReady/onExit回调都会从 group 的snapshots()取全量快照做整文件同步重写writeServeServiceInfo使用openSync/writeSync且无await同步完成即原子落地绝不做单条目的增量读-改-写——否则 N 个 worker 的并发回调会互相丢失更新。六、要点 5单工作区线形协议不变多工作区字段均为附加计划第五条要求在保持单工作区 wire shape 不变的前提下为多工作区模式新增经过校验的 per-workspace pidfile、daemon-status、诊断与 SDK 字段。具体兼容性约定pidfileServiceInfo顶层保留channels所有 worker channels 的并集与workerPidprimary worker 的 pid旧读取方只读这两个字段的qwen channel status不受影响新增可选workers?: Array{ workspaceId?; workspaceCwd?; channels: string[]; workerPid? }数组供新读取方使用。run-qwen-serve.ts 中的writeServeServiceInfo相应获得可选workers参数写入时沿用既有O_RDWR O_NOFOLLOW serve 所有权保护。daemon statusDaemonStatusRuntime新增可选channelWorkers数组每个快照附带workspaceId/workspaceCwd/primary既有的channelWorker字段继续承载 primary 快照旧客户端无感。worker envCreateChannelWorkerSupervisorOptions新增可选workerBaseEnv默认process.envgroup 在创建 supervisor 时读取 runtime 的env.effectiveEnv直接透传父进程模式 runtime单工作区该字段为 undefined精确回落到process.env行为与改造前一致。worker 侧校验DaemonCapabilitiesLike新增可选workspaces数组/capabilities自 Phase 2a 起已发布worker 校验自己的 workspace 命中其中某个已信任条目旧单工作区 daemon 未发布该字段时回落到 legacy 的 capabilities.workspaceCwd比较。单工作区行为因此严格不变分组算法在只有一个工作区时只能产生一个 primary 分组与既有行为等价。七、要点 6聚焦测试覆盖矩阵计划第六条列出的测试面在仓库中对应如下文件可按语义层 → 编排层逐层查证覆盖面测试文件相对 cwd 语义、规范化失败、信任校验、歧义/0-owner 判定channel-workspace-grouping.test.tsgroup 的回滚、reconcile 合并、webhook 路由、快照channel-worker-group.test.ts两工作区编排、pidfile 清理、status 诊断、兼容性行为run-qwen-serve.test.tsworker 环境传播workerBaseEnv注入子进程channel-worker-supervisor.test.ts其中 channel-workspace-grouping.test.ts 用loadChannelsConfig注入桩把同一个 user 层 entry 出现在两个工作区的合并配置中但显式cwd把归属钉死在 SECONDARY这类关键场景做成了纯函数断言run-qwen-serve.test.ts 的多工作区编排测试还会从 supervisor 启动侧探测活 daemon 的/capabilities路由防止 runtime/worker 就绪顺序在注入的 ready-only factory 下悄悄退化。八、验证流程计划末尾的 Verification 一节给出了验收路径按顺序执行运行受影响的 CLI 与 SDK 测试上表所列聚焦测试文件优先运行格式化与 lint 检查执行仓库构建与类型检查npm run build、npm run typecheck实际演练一个两工作区 daemon 托管 channel的场景对最终 diff 做三项专项审计生命周期竞态lifecycle races、兼容性回归compatibility regressions、以及是否存在不必要的 API 面unnecessary API surface。第五项审计呼应了计划开头的目标约束——本次补强不应引入任何 workspace-qualified channel 控制 API新增字段全部是附加式的新增行为全部收敛在既有的分组、启动、重载与状态上报路径内。九、小结这份 follow-ups 计划的价值在于把多工作区 channel worker从功能可用推进到故障路径可证明解析与校验共用一条 cwd 解析链消除了两侧结论分叉的可能listen 期的冻结计划消除了启动过程中的配置快照漂移group 级回滚与 reconcile 事务消除了半启动/半重载的 fleetruntime 发布时序与全量 pidfile 重写消除了就绪顺序与并发写丢失问题附加式协议字段则让单工作区用户与旧客户端完全无感。对维护者而言若后续要改动--channel的解析、分组或 worker 生命周期channel-workspace-grouping.ts、channel-worker-group.ts 与 run-qwen-serve.ts 及其对应测试文件是必读的最小集合对运维者而言本文第二节与第三节给出的四种错误码与回滚语义也是排查 daemon 启动 channel worker 失败时的直接依据。【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考