LobeHub 多维代码评审之 Compatibility 维度:如何守住主题、平台、客户端版本与部署形态的兼容性矩阵
在 LobeHub 这类同时交付桌面应用(Electron)、Web 桌面端、Web 移动端与 React Native 客户端、并且既支持云版本又支持开源自托管的产品中,一次改动往往需要同时在多个运行表面上正常工作。本文基于 LobeHub 仓库中多维代码评审技能(deep-review)下的 Compatibility 维度规则文件,完整讲解"兼容性矩阵"的七个坐标轴、配套的快速检查清单(Quick Checklist),以及评审时如何逐项核查与判定"违规/非违规"。读完后,你将掌握一套可落地的兼容性自查方法:在提交或评审 PR 前,如何系统性地排查主题(浅色/深色)、平台、客户端版本、Agent 运行时位置、部署形态、版本(edition)与租户(tenancy)七个维度上的回归风险,并理解 LobeHub 如何用成对的路由配置与自动化测试来固化其中最容易出错的"路由配对"不变量。
一、Compatibility 维度在 deep-review 技能中的定位
Compatibility 是 LobeHub deep-review 技能定义的多个评审维度之一。每个维度对应 references/dimensions/ 目录下的一个 Markdown 文件,文件名即维度名,文件头部用 frontmatter 声明其 id_prefix、verify(发现是否需要独立 verify 子代理做真伪判定)以及 skip_when(何种 diff 可跳过该维度)。Compatibility 的 frontmatter 如下:
---
id_prefix: compat
verify: true
skip_when: no UI theming/routing, no API contract, no deployment config, no data-scoping/permission logic, no runtime-branching code touched
---
id_prefix: compat:该维度产出的发现(finding)统一使用compat前缀编号。verify: true:Compatibility 的发现会经过独立 verify 子代理逐一证伪,返回confirmed/false_positive/need_more_context三态判定——这与 deep-review 技能 中"反幻觉"核心原则一致,候选发现必须由读取完整上下文的独立验证者确认,而不是凭置信度百分比过滤。skip_when:当 diff 未触及 UI 主题/路由、API 契约、部署配置、数据作用域/权限逻辑或运行时分支代码时,可整体跳过该维度。这一条同时被 deep-review 的剪枝表 收录——剪枝表明确写着"compatibility:diff touches no UI theming/routing, no API contract, no deployment config, no runtime-branching code"时跳过。
Compatibility 维度在 deep-review 技能 的维度总表中覆盖范围被概括为:"light/dark theme, desktop app / web (desktop, mobile) / RN, released-client API compatibility, client vs server agent runtime (gateway on/off), Vercel vs Docker deploys, paired router configs",其 Verified? 列为 yes,意味着它的发现会进入 verify 流水线。
理解这一点很重要:Compatibility 不是"泛泛介绍项目支持哪些平台",而是一条可执行的评审规则——它告诉评审者(作者和 reviewer 都会用)如何判断"这次 diff 是否会让某个矩阵单元悄悄退化"。
二、核心问题:开发者只验证自己那一格
维度文件开篇点明了 Compatibility 要解决的普遍缺陷:
同一个改动必须在产品交付的每个表面上正常工作。作者(和 reviewer)习惯性地只验证自己的开发环境——通常是 云版本 + Web 桌面端 + 浅色模式 + 最新客户端 + 个人工作区——而矩阵里的每一个其他单元格,才是回归问题藏身之处。
这正是多客户端、多部署形态产品的典型盲区:本地 pnpm dev 跑在最新代码、云版本、浅色主题、个人上下文上时一切正常,但已发布的桌面/移动客户端仍可能调用旧接口,自托管用户的 business slot 返回的是 no-op 默认值而非云端的商业覆写,Electron 与 Web 的路由树又不完全一致。Compatibility 维度就是把"我本地能跑"升级为"整张矩阵都能跑"。
三、兼容性矩阵:七个坐标轴
维度文件的核心是一张"必须始终记在脑子里"的兼容性矩阵。以下完整继承该矩阵的每一行,并结合 LobeHub 仓库的源码组织展开说明。
| 坐标轴 | 需要记住的变体 |
|---|---|
| Theme(主题) | 浅色 / 深色(cssVar.* token 同时覆盖两种;硬编码颜色会打破其中一种) |
| Platform(平台) | 桌面应用(Electron)/ Web 桌面端 / Web 移动端 / React Native |
| Client version(客户端版本) | 服务端部署后,已发布的桌面/移动客户端仍会继续调用旧端点 |
| Agent runtime(Agent 运行时) | 客户端运行时 vs 服务端运行时(gateway 开启与否) |
| Deployment(部署) | Vercel(serverless:无本地文件系统持久化、执行时长受限)vs Docker(长驻进程) |
| Edition(版本) | 开源自托管(business slot 返回安全 no-op 默认)vs 云(商业覆写生效) |
| Tenancy(租户) | 个人上下文(workspaceId === null)vs 工作区上下文(workspace 作用域的数据、权限、成员共享) |
逐轴解读,并给出仓库中的对应证据:
3.1 Theme:cssVar.* token 而非硬编码颜色
浅色/深色不是简单的"两套样式",而是一套由 CSS 变量 token 驱动的体系。LobeHub 的 react 组件技能 在 Styling 一节明确了样式方案优先级:多数场景用 createStaticStyles + cssVar.*(零运行时、模块级),只有在真正需要 JS 动态计算颜色(如 readableColor / chroma)时才退而使用 createStyles + token。cssVar.* 之所以能同时覆盖两种主题,是因为它引用的是随主题切换而变化的 CSS 变量;一旦写成硬编码的十六进制颜色或只适配浅色的资源图,就会在深色模式下断裂。这正是 Compatibility 检查清单第一条"硬编码颜色或 light-only 资源——会破坏深色模式,应使用主题 token"的底层依据。
3.2 Platform:Electron / Web 桌面 / Web 移动 / RN
LobeHub 的 SPA 入口按平台拆分,可参见 spa-routes 技能 描述的入口结构:src/spa/entry.web.tsx、src/spa/entry.mobile.tsx、src/spa/entry.desktop.tsx(对应 Web 桌面、Web 移动、Electron 桌面)。桌面端与 Web 桌面端虽同为"desktop",但路由树并非完全相同——Electron 的根树持有 TabHost 存根、真实内容路由在 per-tab 内存路由中构建,而 Web 端则有 /verify-im、/onboarding 等 Web 专有路径。因此"在 Web 桌面端加一个页面"不等于"在 Electron 桌面端也加好了",这正是矩阵里 Platform 轴要求逐一核对的原因。
3.3 Client version:旧客户端仍调用旧端点
服务端部署新版本后,已发布的桌面/移动/RN 客户端并不会立刻升级,它们仍会按旧契约调用接口。因此"当前代码里已经找不到某个端点的调用方"不等于"没有任何客户端还会调用它"——这是 Compatibility 维度反复强调的关键判断。
3.4 Agent runtime:客户端 vs 服务端(gateway 开关)
LobeHub 的 Agent 既可在客户端运行时执行,也可在开启 gateway 的服务端运行时执行。凡是隐含假设了"Agent 在某一侧运行"的逻辑,都必须显式分支或保持运行时无关(runtime-agnostic),否则在 gateway 开/关两种状态下行为会分叉。
3.5 Deployment:Vercel serverless vs Docker 长驻
同一份服务端代码可能部署在 Vercel(无本地文件系统持久化、函数执行时长受限)或 Docker(长驻进程)。会写本地文件、假设进程长期存活、或在单次请求间用内存态维持数据的代码,在一侧正常、另一侧失败。
3.6 Edition:开源自托管的 no-op 默认 vs 云的商业覆写
LobeHub 同时发行开源自托管与云两个版本。开源版本中,商业化 business slot 返回"安全的 no-op 默认值",而云版本启用商业覆写。仓库中可以看到这一模式的直接证据:useActiveWorkspaceId 的开源默认实现就是
export const useActiveWorkspaceId = (): string | null => null;
即开源构建下工作区 id 恒为 null。任何"只有当某 business slot 有云覆写时才工作"的逻辑,都必须能在 slot 返回开源 no-op 默认时依然正确运行(功能隐藏或优雅降级,而不是报错崩溃)。
3.7 Tenancy:个人上下文 vs 工作区上下文
数据读写要区分两种作用域:个人上下文(workspaceId === null,自托管/个人)与工作区上下文(workspace 作用域的数据、权限、成员共享)。新写的读取、权限检查或列表作用域若悄悄假设了"个人上下文",就会在工作区场景下越权或漏数据。
四、快速检查清单(Quick Checklist)
维度文件的"Quick checklist"是 light 模式评审者直接对照执行的检查项,共 11 条。以下完整继承并逐条结合仓库佐证展开——每条都是"什么改动会破坏哪个矩阵单元"的具体判据。
- 硬编码颜色或 light-only 资源——破坏深色模式,应使用主题 token。依据见上文 3.1 与 react 技能 的 Styling 优先级表。
- 配对路由缺失——新路由/页面注册进了 desktopRouter.config.tsx 却没注册进 desktopRouter.config.desktop.tsx(或反之),会导致白屏;desktopRouter.sync.test.tsx 必须保持绿色。这是 LobeHub 用测试固化的一条"路由配对不变量",详见第五节。
- 移动端变体缺失——页面/功能只加进了桌面路由,没有对应的
(mobile)变体,也没有响应式处理。 - 删除/重命名 TRPC procedure 或 webapi 路由,但已发布客户端仍会调用——需保留一个兼容性别名。对"已废弃的写路径"而言,只有当旧客户端把该成功形状解释为"无事可做"、且没有用户可见状态/计费/权限/删除被错误地报告为已完成时,才允许用一个无副作用的 noop 兜底;对真正的操作,应返回稳定的业务错误(如
PRECONDITION_FAILED/410 Gone),而不是盲目返回成功。 - 改了 API 输入/输出形状却没做版本化或可选字段回退——旧调用方会拿到错误形状。
- 假设了 Agent 运行时位置的逻辑——必须分支或保持运行时无关(对应 3.4 的 gateway 开关)。
- 敌意 serverless 代码——本地文件写入、假设跨请求存活的内存态、超出函数时长限制的长时任务(对应 3.5)。
- 无视 workspace 的逻辑——新数据读写、权限检查、列表作用域悄悄假设个人上下文——必须尊重当前工作区作用域(
useActiveWorkspaceId/workspaceSlug),或明确设计为仅个人可用。 - 云版本专属假设——只有当某 business slot 有云覆写才工作的逻辑——必须还能在 slot 的开源 no-op 默认下工作(功能隐藏或优雅降级,而非崩溃,对应 3.6)。
- 重命名后端路由路径或认证入口,或改动
@lobechat/business-*导出——下游部署会覆写/扩展这些路径,需在 PR 中显式标记以便下游适配。仓库中这些被下游依赖的路径包括src/app/(backend)/webapi/...(后端 webapi)、src/app/spa-auth/...(SPA 认证)与src/routes/auth/...(认证页面路由,如 signin/signup/oauth/reset-password 等)。 - 依赖主版本升级(如
next、drizzle-orm等)——下游需要同步(lockstep)升级,必须在 PR 描述中说明。
五、路由配对不变量的源码级佐证
检查清单第 2 条是整个维度里最具"可验证"特征的一条,因为它背后有一条明确的自动化测试。LobeHub 的桌面路由由两个平台适配器 + 一个共享定义构成:
- desktopRouter.config.tsx —— Web 平台的桌面路由适配器;
- desktopRouter.config.desktop.tsx —— Electron 平台的桌面路由适配器;
- 共享的路径/嵌套/懒加载/preload 分组则应放在
desktopRouter.shared.tsx,两个desktopRouter.config*只是薄适配层。
spa-routes 技能 把这条约定写成了硬性 Agent 约束:"Common Web/Electron paths, nesting, metadata, lazy loaders, and preload groups belong in src/spa/router/desktopRouter.shared.tsx. The two desktopRouter.config* files are thin platform adapters; change them only for genuine runtime differences. Do not duplicate a common route in both adapters."
而 desktopRouter.sync.test.tsx 则用可执行断言把"两棵树结构一致"这一不变量钉死。该测试的关键断言是:
it('generates identical main-area path and nesting behavior for Web and Electron', () => {
expect(routeShape(createElectronMainAreaChildren())).toEqual(
routeShape(createWebMainAreaChildren()),
);
});
它通过 routeShape() 递归抽取 path / index / children,要求 Web 与 Electron 的"主区域"路由形状完全相等。测试还用 matchRoutes 对大量路径(如 /agent/agent-1/stats、/settings/memory、/acme/settings/provider)逐一验证命中与参数,从而确保两个平台适配器对同一批路径行为一致。此外它还对路由源文件做静态文本断言(例如 preloadId: 只应出现在共享定义中,两个平台文件都不应包含),防止把共享逻辑重复进某个适配器。
这意味着:当你在 Web 适配器里新增一条路由而忘了在 Electron 适配器(或共享定义)里补上时,routeShape 相等断言会立刻失败、测试变红。Compatibility 清单里"desktopRouter.sync.test.tsx must stay green"正是把"人工记得两边都改"升级为"CI 替你守这条线"。
六、How to check:四条核查步骤
维度文件给出评审时的四条具体操作,这里完整继承并标注其针对的矩阵轴:
- 对每个被删除/重命名的导出、路由、procedure:先在当前代码中搜索调用方,再检查是否有"已发布"客户端(桌面/移动/RN)仍可能调用它——当前代码里找不到不是证据。这对应 Client version 轴,也呼应 3.3 的"当前代码缺失≠无调用方"。
- 对 UI 改动:读样式找硬编码颜色;路由有变时检查两份路由配置;查找移动端对应物。对应 Theme / Platform 轴。
- 对服务端改动:扫描是否存在文件写入、定时器、假设进程长命的内存缓存。对应 Deployment 轴(serverless 友好性)。
- 对数据/权限逻辑:追踪
workspaceId为null(个人/自托管)以及相关业务 slot 返回开源默认值时各会发生什么——两条路径都必须保持健全。对应 Tenancy 与 Edition 轴,useActiveWorkspaceId在开源构建下返回null正是这一"两条路径都要走通"要求的现实形态。
维度文件还给出"规则来源(deep mode 评审前先读)":spa-routes 技能 讲路由配对不变量与 mobile/desktop 变体;react 技能 讲主题/token 规则;并在删除端点前,先对旧端点做 git log -p 查看历史,弄清"是谁移除了调用方、已发布客户端是否早于该改动",再判断该 procedure 是否真的未被使用。
七、Violations 与 Not violations:如何划定判定边界
最后两条规则决定了"什么算违规、什么不算",避免评审者过度报警或漏报:
- Violations(违规):本次 diff 明确导致、或悄悄退化了矩阵中某个坐标轴的,即为违规。强调的是"由本 diff 引入"——既有代码里的既有缺陷不算本次发现。
- Not violations(非违规):
- 改动面根本无法触及的坐标轴。例如一次纯服务端重构,就不需要去检查深色模式;
- 在 PR 描述中已显式协调的破坏性变更(迁移窗口、配套客户端发布)。此时要核实协调确实被写明,然后最多留一条 note 级别发现,而不是按缺陷处理。
这两条与 deep-review 技能 "按代码库现状与其生命周期校准"的核心原则一脉相承:把 diff 放到它所处的既有标准上衡量,而不是理想化的标准。
八、把 Compatibility 用起来:一个可复用的自查流程
综合以上,把 Compatibility 维度落到日常开发,可以按如下顺序自查(覆盖七个坐标轴 + 十一条清单):
- 判定维度是否适用:diff 是否触及 UI 主题/路由、API 契约、部署配置、数据作用域/权限逻辑、运行时分支代码?若全不触及,按
skip_when跳过。 - 逐轴过一遍矩阵:主题(有无硬编码颜色)、平台(Electron/Web 桌面/Web 移动/RN 是否都覆盖)、客户端版本(删改的端点是否被旧客户端调用)、Agent 运行时(gateway 开/关两态)、部署(serverless 敌意代码)、版本(开源 no-op 默认下是否可用)、租户(
workspaceId === null与工作区两条路径)。 - 对路由改动,确认两份
desktopRouter.config*与共享定义一致,且desktopRouter.sync.test.tsx保持绿色。 - 对 API/端点删除或重命名,先搜当前调用方、再核对已发布客户端、必要时用
git log -p回溯历史,并为破坏性变更在 PR 描述中写明协调方式。 - 最后用 Violations / Not violations 两条规则给每条候选发现定性,避免把"改动触及不到的轴"或"已协调的破坏性变更"误报为缺陷。
这套流程的价值在于:它把"兼容性"从一个笼统的形容词,收敛成一组可逐项打勾、且有测试与源码证据支撑的具体判据。在 LobeHub 这种多客户端、多部署形态、开源/云双版本的项目里,遵循 Compatibility 维度的矩阵与清单,是防止"我本地能跑、别人那崩了"这类回归的最直接防线。
参考的仓库文件
- Compatibility 维度规则:.agents/skills/deep-review/references/dimensions/compatibility.md
- 技能总览(维度表、剪枝表、核心原则):.agents/skills/deep-review/SKILL.md
- 路由配对不变量与 mobile/desktop 变体:.agents/skills/spa-routes/SKILL.md
- 主题/token 规则:.agents/skills/react/SKILL.md
- 配对路由测试:src/spa/router/desktopRouter.sync.test.tsx
- 两份桌面路由适配器:src/spa/router/desktopRouter.config.tsx、src/spa/router/desktopRouter.config.desktop.tsx
- 开源 no-op 工作区默认实现:src/business/client/hooks/useActiveWorkspaceId.ts
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00