Appearance
菜单两套导航机制,AI 难重写
状态: 🟢 已修复
日期: 2026-07-12 严重程度: 🟠 P2(架构缺陷,影响代码维护和 AI 协作) 影响范围: frontend/src/menus/(16,345 行 / 41 个文件) 发现方式: 代码审核(ADR-093 背景分析) 修复方案: menu-schema.ts + menu-factory.ts 声明式 MenuNode + 统一渲染器,57 个面板迁移
问题描述
联邦的菜单导航有两条路径:
Record<string, () => PopupLevel>— 静态路由表。点菜单文件夹时查这张表,找到对应的 build 函数。getXMenu()?.push(buildX(id))— 动态 push。点了某个道具的详情,把新层级推入栈。
两条路径,两套代码。静态的走 Record,动态的走 push。
AI 想重写菜单时,必须先 trace 全部 41 个文件,才能理解"有哪些菜单"。因为菜单树不是一份数据,它是 35+ 个 build*Level() 函数的执行结果。
根因分析
这是历史演化的结果。早期菜单只有静态路由,Record 够用。后来需要动态实例详情(点了某个模型的详情),push 被引入。
push 不是 bug——它是合法的导航原语。但它和 Record 没有统一于同一份数据源。AI 无法通过读取一份文件就理解当前菜单结构。
为什么没有暴露
"AI 难重写"不是用户感知的 bug。用户用菜单,没有 AI 参与。菜单能用,功能对,没有报错。
但"能用"和"可维护"之间的距离,是一次架构演进的鸿沟。
修复方案
src/menus/menu-schema.ts 和 src/menus/menu-factory.ts:声明式 MenuNode + 统一渲染器。
每个节点有 id——稳定的、唯一的。路由由 id 派生,不再需要魔法字符串。StatePath 前缀体系让控件绑定类型安全。
命令式 builder 降级为"自定义层渲染后端"。表单类特殊层保留命令式写法,标准层从 schema 派生。
57 个面板迁移。
教训
- 两套机制并存时,"没有 bug"不等于"架构正确" — 代码功能对,但结构错
- AI 可维护性是架构质量的一个指标 — 如果 AI 看不懂,人类也迟早看不懂