Skip to content

菜单两套导航机制,AI 难重写

状态: 🟢 已修复

日期: 2026-07-12 严重程度: 🟠 P2(架构缺陷,影响代码维护和 AI 协作) 影响范围: frontend/src/menus/(16,345 行 / 41 个文件) 发现方式: 代码审核(ADR-093 背景分析) 修复方案: menu-schema.ts + menu-factory.ts 声明式 MenuNode + 统一渲染器,57 个面板迁移


问题描述

联邦的菜单导航有两条路径:

  1. Record<string, () => PopupLevel> — 静态路由表。点菜单文件夹时查这张表,找到对应的 build 函数。
  2. 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.tssrc/menus/menu-factory.ts:声明式 MenuNode + 统一渲染器。

每个节点有 id——稳定的、唯一的。路由由 id 派生,不再需要魔法字符串。StatePath 前缀体系让控件绑定类型安全。

命令式 builder 降级为"自定义层渲染后端"。表单类特殊层保留命令式写法,标准层从 schema 派生。

57 个面板迁移。

教训

  1. 两套机制并存时,"没有 bug"不等于"架构正确" — 代码功能对,但结构错
  2. AI 可维护性是架构质量的一个指标 — 如果 AI 看不懂,人类也迟早看不懂