# AR09 对象命名规范(Object Naming Standard) > **版本**:V1.0 > **日期**:2026-09-17 > **性质**:规范性文件(normative)。「必须 / 禁止」是硬约束,「应当」是默认做法,「建议」可依场景取舍。 > **与 TOP_CODING_RULES.md 的关系**:G03(变量命名锚定)给的是四条底线;本文件是它的展开与可执行化。 > **冲突时以 TOP_CODING_RULES.md 为准**,本文件不得放宽 G03。 > **适用范围**:后端 Go、数据库、API、前端 Vue、注释与文档。 > **读者**:写代码的人 + 执行命名清理的 AI。 --- ## 一、原理:为什么命名值得单独立一份规范 ### 1.1 命名是唯一零成本的文档 注释会过期,文档会失联,只有名字跟着每一次调用出现在读者眼前。 一个名字读错,读者付出的不是"多看一眼",而是**先建立错误的心智模型,再用后续所有代码去纠正它**。 ### 1.2 名称即契约 大多数命名失误只是难读,但有一类会**静默断链**:当某个标识符是靠名字派生、靠名字配对、靠名字跨进程传递时,改名就等于改协议,而编译器与测试都不会拦。 本项目已经存在的这类协议: | 靠名字建立的协议 | 位置 | 改名后果 | |---|---|---| | `action_key` 由 `action_title`(中文)派生,前后端靠它配对 | `specialistFlow.js` 的 `findLatestRun` | 改文案 → 配对静默失效,UI 永远显示"未执行" | | 路由 query 参数 `specialist/skill/prompt` | `appCatalog.js` → `SmartAssistantPage.vue` | 写读不同名 → 参数被静默丢弃(见 §8.1) | | 专员 / 技能的 `key` | `specialist.key`、`skill_definition.key`、`ValidSkillKeys` | 改 key → 任务挂载、技能绑定、种子数据全部对不上 | | 种子数据的 key | `seedSpecialistRuleFiles` 等 | 拼错 → 启动时只能靠显式校验挡住,否则静默漏种 | **所以:改名前必须先问"这个名字有没有被别人当协议用"。** 这是本文件第七节的由来。 ### 1.3 认知债会复利 一个失真的名字不会自己消失。它会被复制(新代码照着写)、被引用(更多地方依赖这个错名)、被解释(文档里加一段"注意这里其实是指……")。 三个月后,改它的成本是今天的十倍。**失真名字的唯一低成本修复窗口,是它刚被写下的那一刻。** ### 1.4 命名错误逃得过编译器和测试 拼写错误的变量名编译不过,语义错误的名字编译得过。 `const app = specialistCatalog.getByKey(key)` 一切正常,只是每个读它的人都要在脑子里纠正一次。 **这类错误没有任何自动化工具能替你发现——除非专门写一个(见第六节)。** ### 1.5 所以要在"新增"那道口子上拦 追改历史代码性价比低。命名规范的绝大部分价值,发生在**新代码被写下的那五秒钟**:选名字时对照一次术语表。 --- ## 二、判据:一个名字好不好,用这五条量 | 判据 | 含义 | 反例 | |---|---|---| | **准确性** | 名字指向的对象,和它实际装的东西是同一个 | `businessApps` 装的是专员 | | **唯一性** | 一个概念全项目只有一个名字;一个名字只指一个概念 | `contract-review` 既是专员 key 又是技能 key | | **一致性** | 同类东西的写法统一(`ID` 就永远 `ID`) | `UserID` 与 `mediaId` 并存 | | **可检索性** | 能被 grep 精确找到 | `id`、`data`、`res`、`info` | | **稳定性** | 不会因为阶段变化而变成谎言 | `newUser`、`tempData`、`userList2` | ### 冲突时的优先级 ```text 外部标准 > 一致性 > 准确性 > 书写简短 ``` - **外部标准最高**:第三方规范定义的字段名(金蝶的 `FCustId`、OOXML 的 `sldId`、HTTP 头)**必须按字符照抄**,不许"美化"。美化等于自建一层翻译,且翻译层迟早漏项。 - **一致性高于准确性**:`ID` 和 `Id` 哪个更好看无关紧要,全项目只能有一个。两种写法并存时,读代码的人每次都要停下确认"这是同一个东西吗"。 - **准确性高于简短**:宁可名字长一点,也不要让人误判对象边界。 --- ## 三、对象术语表 ### 3.1 正式术语(必须使用) | 对象 | 正式名 | 代码落点 | 说明 | |---|---|---|---| | 专员 | `specialist` | `model/specialist.go` | 可挂载到任务的数字员工 | | 技能 | `skill` | `model/skill_definition.go` | 任务级能力定义 | | 应用 | `app` | `model/app_definition.go` | 长程任务运行壳 | | 连接器 | `connector` | `internal/connector/*` | 外部系统接入 | | 动作 | `action` | `model/action_definition.go` | 技能调用的底层执行单元 | | 任务 | `task` | `model/worker_task.go` | 工作实例容器 | ### 3.2 兼容术语(限制使用,禁止扩散) | 历史词 | 现状 | 允许出现的位置 | 禁止 | |---|---|---|---| | `role` | 旧心智残留 | 无。已无正式对象叫 role | 不得用于新变量、新文件名、新字段 | | `assistant` | 只指"默认通用助手"这一件事 | `assistant.js`(默认聊天接口)、`smart-assistant`(默认技能 key)、`general-assistant`(默认专员 key) | 不得用来指代专员、技能或对象类型 | | `capability` | 泛能力总称 | 仅限口语与文档泛指 | 不得充当对象级文件名或字段名 | | `businessApps` | 历史静态变量,装的是专员 | 现有代码 | 禁止新增引用;建议整块删除(见 §8) | | `availableSkills` | 静态 fallback 技能源 | 现有代码 | 禁止新增引用;建议改名 `staticSkillCatalog` | | `worker` | 任务执行子系统前缀 | `worker_task` / `worker_run` / `worker_artifact` / `/api/worker/*` / `workerRuntime.js` | **待拍板**(见 §3.4) | ### 3.3 共名冲突登记 同一个字符串同时是两个对象的标识符 —— 这是既成事实,不强行改,但要靠注释和测试守住,**并且不再新增**: | 字符串 | 身份一 | 身份二 | |---|---|---| | `contract-review` | 专员 key | 技能 key | | `report-generation` | 专员 key | 技能 key | 处理要求: 1. `model/skill_keys.go` 必须保留说明注释(已有)。 2. 任何"按 key 查对象"的代码,必须写明查的是哪一类,不允许出现 `getByKey(key)` 这种不区分对象类型的调用。 3. `skill_keys_test.go` 那种"前后端 key 集合双向对齐"的测试必须保留 —— 它是这两个身份不互相污染的唯一保障。 ### 3.4 待拍板:`worker` 这个命名空间 现状:`/api/worker/tasks`、`model/worker_task.go`、`worker_run.go`、`worker_artifact.go`、`api/worker.js`、`store/workerRuntime.js`。 `worker` 不在正式术语表里,但已经形成一整套一致的前缀。两个选择: - **A. 收编为正式术语**:定义为"任务执行子系统",写进 §3.1。成本为零,立刻合法。 - **B. 重命名**:把 `worker_*` 收敛为 `task_*`。语义更准,但要动表名、API 路径、前端模块,成本高。 **建议 A**。理由:它已经自洽,且不与任何人抢名字;真正会误导的是 §8 里那些"名实相反"的(`businessApps` 装专员、`capability` 装 skill),而不是一个自洽的子系统前缀。 --- ## 四、分层规范 ### 4.1 后端 Go | 项 | 规范 | 禁止 | |---|---|---| | 类型名 | `CamelCase`,对象名 + 用途后缀:`Specialist`、`SkillDefinition`、`WorkerTask` | `Data`、`Info`、`Manager`、`Helper` 这类空词 | | 字段名 | `CamelCase` | 业务字段用 `Id`/`Data`/`Res` 这类模糊名 | | **`ID` 写法** | **一律 `ID`**:`UserID`、`TaskID`、`RouteID`、`AIRouteID` | `UserId`、`mediaId`、`sourceId` | | json tag | **一律 snake_case**:`json:"specialist_key"` | `json:"skillKey"`、`json:"createdAt"` | | 外部标准字段 | 原样保留,并在字段注释里写明来源系统 | 美化、改大小写、加前缀 | | 常量 | `CamelCase`,同族常量同前缀 | 裸数字、魔法字符串 | | 接收器 | 与类型同义的单字母或短名(`s`、`req`) | 无意义的 `this`、`obj` | **允许的例外(白名单,只有这些)**: - 第三方 API 字段:`FCustId`、`FUseOrgId`、`FStockId` 等 ERP 字段 - 文档格式规范属性:OOXML 的 `sldId`、`styleId` 例外必须在字段旁注明来源,否则下一个人会以为是漂移。 ### 4.2 数据库 | 项 | 规范 | |---|---| | 表名 | 单数 snake_case:`specialist`、`skill_definition`、`worker_task` | | 列名 | snake_case,与 Go 字段的 json tag 一致 | | 外键 | `<对象>_id`:`specialist_id`、`task_id` | | 时间 | `<动词>_at`:`created_at`、`finished_at` | | JSON 列 | 后缀 `_json`,并在字段注释写明结构(如 `allowed_skills` 存的是字符串数组) | | 布尔列 | 语义正向:`enabled`、`pinned`;禁止 `not_xxx` | | 改名 | **`AutoMigrate` 不会改列名**,必须手写迁移(P06.1) | ### 4.3 API | 项 | 规范 | 现状 | |---|---|---| | 路径 | 复数资源:`/api/specialists`、`/api/skills`、`/api/apps` | ✅ 已一致,保持不回退 | | 路径参数 | snake_case:`/api/knowledge/audit/{source_id}` | ❌ 现在是 `{sourceId}`、`{recordId}` | | query 参数 | snake_case,且**跨层同名** | ❌ 见 §8.1 | | 入口协议 | 对象入口 query 必须**定义在一处常量**,写读都引它 | ❌ 现在两边各写各的 | **入口协议强制要求**:任何"从 A 页面带参数打开 B 页面"的协议,参数名必须是**共享常量**,不允许两边各写字符串字面量。这是 §8.1 事故的直接教训。 ### 4.4 前端 | 项 | 规范 | |---|---| | 文件名 | store:`<对象>Catalog.js`(如 `specialistCatalog.js` / `skillCatalog.js` / `appCatalog.js`);API:`<对象>.js`(如 `specialist.js` / `worker.js`) | | catalog store 模板 | `normalize` → `hydrate` → `getByKey` / `getByPath`(`specialistCatalog.js` 为标准范本) | | computed | `currentPresentation`,对象名必须准确 | | 局部变量 | **必须与对象一致**。`const app = specialistCatalog.getByKey()` 是禁止的 | | 布尔 | `is` / `has` / `can`,禁止否定式 | | 容器 | 单个 `item`,集合 `items`,映射 `Map`,列表 `List` | ### 4.5 注释与文档 - 历史兼容字段必须在注释里写明"历史兼容 / 待迁移",否则会被当成正式命名复制。 - 文档里的文件引用**用相对路径**,禁止 `file:///home/<某人的用户名>/...` 这类绝对路径 —— 别人 clone 下来全是死链(违反 G18)。 --- ## 五、命名模式库(做法) ### 5.1 后缀表 | 后缀 | 含义 | 例 | |---|---|---| | `_id` | 主键 | `task_id` | | `_key` | 业务标识(字符串,可读) | `specialist_key`、`action_key` | | `_at` | 时间点 | `finished_at` | | `_count` | 计数 | `retry_count` | | `_json` | 存 JSON 的文本列 | `allowed_skills`、`input_json` | | `_ms` / `_bytes` | **单位必须进名字** | `timeout_ms`、`size_bytes` | **单位不写进名字是隐性 bug 源**:`timeout` 是秒还是毫秒?读的人只能翻实现。 ### 5.2 布尔 `is` / `has` / `can` / `should` + 正向语义。禁止 `flag`、`status`(名词当布尔用)、`notXxx`(双重否定)。 ### 5.3 容器 `item`(单) / `items`(集) / `xxxMap`(映射) / `xxxList`(有序)。让读者不点进定义就知道能不能 `.map()`。 ### 5.4 函数动词表 同一件事只用一个动词,禁止同义混用: | 动词 | 职责 | 禁止替代 | |---|---|---| | `normalize` | 把外部来的数据整理成内部统一形状 | `format` / `convert` / `adapt` | | `hydrate` | 把目录/缓存数据装进运行时 | `load` / `init` | | `resolve` | 按规则算出一个结果(可能多来源) | `get` / `find` | | `load` | 从存储读 | `fetch` / `read` | | `build` | 组装(无 IO) | `create` / `make` | | `ensure` | 没有就补上(幂等) | `check` / `init` | | `apply` | 把某份数据/规则作用到目标上(幂等) | `update` / `set` | | `parse` | 字符串 → 结构 | `decode` / `read` | ### 5.5 禁止词表 作为**业务标识符**禁止使用(框架/标准库要求时除外): `id`、`data`、`info`、`res`、`req`(局部除外)、`tmp`、`temp`、`obj`、`item`(无上下文时)、`list`(无前缀时)、`util`、`common`、`helper`、`manager`、`handler`(无对象前缀时)、`flag`、`value` 理由:不是难看,是**搜不到**。grep 出几百条结果等于没有结果(G03 的最小长度约束就是这个意思)。 ### 5.6 缩写表 只有下表内的缩写允许使用,其余一律写全: | 允许 | 全称 | |---|---| | `ID` | identifier | | `URL` | uniform resource locator | | `API` | application programming interface | | `JSON` | JavaScript object notation | | `AI` | artificial intelligence | | `OCR` | optical character recognition | | `PPT` / `PDF` | 文件格式 | | `req` / `res` | 仅限 HTTP 处理函数的局部变量 | 禁止:`usr`、`mgr`、`svc`、`num`、`str`、`val`。(HTTP 处理函数的局部 `req` / `res`、Go 的 `ctx`、局部配置 `cfg` 属公认短名,不算违规。) --- ## 六、如何检查 ### 6.1 人工五问(写名字时问自己,review 时问作者) 1. 这个名字指的是**哪个对象**?能一句话说出它是 specialist / skill / app / connector / action / task 中的哪一个吗? 2. 换个人读,会不会理解成**另一个对象**? 3. 全项目 grep 一下,**是不是只有这一种写法**? 4. 改它会不会断掉**靠名字派生的协议**(§1.2 的表)? 5. 它是**外部标准**吗?是 → 按字符照抄,一个字都不许动。 ### 6.2 机器守卫(建议落成一个 Go test,与 `skill_keys_test.go` 同一位置) 已有先例:`model/skill_keys_test.go` 读 `frontend/src/config/workbench.js`,把前端技能 key 与后端 `ValidSkillKeys`(22 个)双向对齐。 **命名守卫照这个模式写**,每条都要带白名单,否则会被误报淹没: | # | 查什么 | 正则 | 白名单 | |---|---|---|---| | A | Go 里 `Id` 写法 | `\b[A-Za-z]+Id\b` | ERP 字段(`FCustId` 等)、OOXML(`sldId`、`styleId`) | | B | camelCase json tag | `json:"[a-z]+[A-Z]` | 无 | | C | camelCase 路径参数 | `\{[a-z]+[A-Z][A-Za-z]*\}` | 无 | | D | 禁用词当业务标识符 | 按 §5.5 词表 | 框架约定(`req` 局部) | | E | 前端 skill key ↔ 后端 `ValidSkillKeys` | 已有的 `skill_keys_test.go` | — | | F | 入口协议写读对称 | 见下 | — | **守卫 F 的做法**(最重要,也最难自动化): 入口协议不要靠扫描源码配对,而是**把参数名提成共享常量**(如 `frontend/src/config/objectEntry.js` 导出 `ENTRY_PARAMS = { specialist: 'app_specialist', ... }`),写端读端都引它。 这样 F 就从"看不见的约定"变成了"编译器能查的引用",守卫 F 也就不需要了 —— **能用结构消除的检查,不要用扫描去补**。 ### 6.3 跨层对齐检查 任何"同一份清单在前后端各写一遍"的东西,都必须有双向对齐测试: - 技能 key:前端 `availableSkills` ↔ 后端 `ValidSkillKeys` ✅ 已有 - 专员 key:前端静态目录 ↔ 后端种子数据 ⬜ 建议补 - 入口协议参数名:⬜ 建议补(提到共享常量后自然消解) ### 6.4 检查结果的严重性分级 | 级别 | 判据 | 处理 | |---|---|---| | **P0** | 名字不一致导致**功能已断**(写读不同名、配对失效) | 当 bug 修,立即 | | **P1** | 跨层漂移,会被新代码复制(json tag、路径参数、失真局部变量) | 本迭代内 | | **P2** | 名字带旧词但语义尚可推断(`RoleKind`、`currentRolePresentation`) | 排期做,别急 | | **P3** | 死代码 / 无人引用的历史变量 | **先判生死,再决定改名还是删除** | **注意 P3 的陷阱**:给一份没人读的静态数据改名,等于给它续命。 先确认引用数,是 0 就删(见 §8.9)。 --- ## 七、如何修复 ### 7.1 迁移顺序:由内到外,最后才碰数据库 ```text ① 局部变量 ② 前端协议 / 入口参数 ③ 文件名 ④ 字段名 / 数据库列名 ``` 理由: - ① 影响面最小、可读性收益最直接、无跨进程风险。 - ② 优先于 ③ —— 协议不统一会**持续产生新的分叉**,不先堵住,你改完还会被新代码重新搞乱。 - ④ 放最后:牵动数据库,而 SQLite 的列改名 `AutoMigrate` 不管,必须手写迁移,风险最高、回滚最难(G05:大规模操作前先备份)。 ### 7.2 改名六步法 1. **定名**:对照 §3 术语表 + §5 模式库,写下新名字,并回答 §6.1 的五问。 2. **查引用**:`grep -rn "<旧名>"`,范围必须包含 - 源码 / 模板 / 样式 - **字符串字面量**(路由、query key、事件名、localStorage key) - **JSON tag、SQL 语句、迁移脚本** - 配置、种子数据、测试、e2e、文档、注释 3. **判协议**:这个旧名是否出现在 §1.2 那张表里?是 → 这不是改名,是改协议,写下来通知所有相关方(含前端)。 4. **一次改完**:不留双写法。兼容层是唯一例外,且**必须带删除条件**("等 X 上线后删除"),否则兼容层会永久化。 5. **验证**(按 G04,不靠感觉): - `go build ./...` + `go test ./...` exit 0 - `npm run build` exit 0 - **真起 dev server,在浏览器里走一遍受影响路径** —— 编译通过不代表功能没断(§8.1 就是编译全绿但功能已断) 6. **提交**:一次提交只装一件事(G14);提交信息写**为什么改名**,不只写改了什么。 ### 7.3 会静默断链的改名(动手前必读) | 改名对象 | 为什么危险 | 必须同时检查 | |---|---|---| | `action_key` / `action_title` | key 由中文标题派生,前后端靠名字配对 | `specialistFlow.js` 的 `findLatestRun`;任何改文案的地方 | | 路由 query 参数 | 写端读端是两处独立字面量,编译不报错 | 全链路 grep,或改成共享常量 | | 专员 / 技能的 `key` | 任务挂载、技能绑定、审计日志全按 key 存 | `worker_task.specialist_key`、`ai_call_log.specialist_key`、种子数据 | | 种子数据的 key | 拼错或改名 → 与库中现有记录对不上 | 启动时校验(`applySpecialistRuleFiles` 已有) | | 外键列名 | 老库有数据,改列名要迁移 | 手写迁移 + 备份 + 副本验证(P06.1/P06.7) | **判断口诀**:这个名字如果在**两个不同的进程/两次运行之间**传递过,它就是协议,不是名字。 ### 7.4 各级的具体做法 **① 局部变量** 直接改,改完读一遍上下文确认没有同名遮蔽(shadowing)。 **② 前端协议 / 入口参数** 不要只是"改一致",而是**提成共享常量**: ```js // frontend/src/config/objectEntry.js export const ENTRY_PARAMS = { specialist: 'app_specialist', skill: 'app_skill', prompt: 'app_prompt', } ``` 写端 `params.set(ENTRY_PARAMS.specialist, ...)`,读端 `route.query[ENTRY_PARAMS.specialist]`。 这样下一个参数不会再各写各的。 **③ 文件名** - 只改名:`git mv`,保持历史可追溯。 - 一文件两职责:先拆文件,再改名,**同时改注册点**(`router.go` 等)。只拆一半会留下一个名实仍不符的残文件,比不改更糟。 **④ 字段名 / 列名** 1. 先备份(`data/backups/` 内置 `VACUUM INTO`,D24)。 2. **在库副本上验证**新列/新列名能被正确读写(P06.1)。 3. 写一次性迁移;老列在确认无引用前不删。 4. 同步改 json tag、前端读取点、文档字段表。 5. 改完立即跑一遍 P06.2 的做法:**探测脚本用完即删**,别留成断言"升级前状态"的测试。 ### 7.5 回滚与提交 - 一次提交一件事:改名与功能改动**不要混在一个提交里**(否则回滚会带走功能)。 - 改名列必须写明"旧名 → 新名",方便 `git log --follow` 与后来人搜索。 - 如果改名波及数据库,提交信息里写明迁移步骤与验证结果。 --- ## 八、本项目现状(2026-09-17 实测) > 以下每条都在代码里核实过,标了行号。级别按 §6.4。 ### 8.1 【P0|功能已断】对象入口协议写读不对称 - **写端**:[appCatalog.js:140-142](../../eai_agentplatform/frontend/src/store/appCatalog.js#L140-L142) 拼 `specialist` / `skill` / `prompt` - **读端**:`SmartAssistantPage.vue:603`、`604`、`619` 读 `app_specialist` / `app_skill` / `app_prompt` - **全仓库核实**:没有任何地方读不带前缀的,也没有任何地方写带前缀的 - **后果**:从能力目录点应用进工作台,预置的专员 / 技能 / 提示词**三个参数全部被静默丢弃**,不报错。用户看到的是"点进去还是空的" - **注意**:这是 bug,不是风格问题。按 §7.4② 提成共享常量一起修 ### 8.2 【P1】camelCase json tag `my_app_center.go:36-41` 六处:`installState`、`skillKey`、`createdAt`、`iconText`、`coverTone`、`isCustomApp`,是全站 snake_case 体系里仅有的例外,且集中在同一个结构体。 → 改 snake_case,前端同步。 ### 8.3 【P1】URL 路径参数 camelCase `api/knowledge.go:97` 的 `{sourceId}`、`api/exam.go:975` 的 `{recordId}`。 → 改 snake_case(`{source_id}`、`{record_id}`),同步前端调用处。 ### 8.4 【P1】局部变量失真 `SmartAssistantPage.vue:314`:`const app = specialistCatalog.getByKey(key)` —— 拿到的是专员,却叫 `app`。 注:它所在的 computed 名字是对的(`currentSpecialistPresentation`,`kind: 'specialist'`),失真仅限这一个局部变量。 → 改 `specialist`。**P1 原因:它会被后续代码照抄。** ### 8.5 【P2】`role` 心智残留 `SmartAssistantPage.vue:354` 的 `currentRolePresentation`,把 assistant / specialist / skill 三类收在一个 role 概念下。 → 收敛为 `currentObjectPresentation`,或按 §3.1 拆成三份。 ### 8.6 【P2】`RoleKind` 字段名带旧词 `model/skill_definition.go:13`:`RoleKind`,注释 `assistant / specialist / skill`。 - **澄清**:这个字段本身不是垃圾,语义是"这条定义属于哪类对象"(`skill_definition` 表同时装 assistant / specialist / skill 三类记录)。问题只在名字里的 `role` 让人误读成"角色"。 - **改名方向**:`object_kind`(准确)。 - **成本提醒**:改字段名要同时动 ① 数据库列 `role_kind` ② json tag ③ 前端读取点。SQLite 改列名 `AutoMigrate` 不管,得手写迁移。 - **建议**:**默认方案是保留字段名,在注释与文档里标注语义**;真要改,按 §7.4④ 走完整流程,不要顺手改。 ### 8.7 【P2】`capability_definition.go` 名实不符 `internal/api/capability_definition.go` 实际装了两套东西:`skillDefinitionReq` 5 个 handler + `actionDefinitionReq` 5 个 handler(共 15 个函数)。 - **不能只改名为 `skill_definition.go`** —— action 的 handler 还在里面,改完名实仍然不符。 - **两个正确方案**:① 拆成 `skill_definition.go` + `action_definition.go`,**同时改 `router.go` 注册**;② 不拆,改名 `skill_and_action_definition.go`。 - **要害**:`capability` 不是正式对象,让它当文件名会持续制造一个不存在的一级概念。 ### 8.8 【P1】`worker` 命名空间待拍板 `/api/worker/tasks`、`worker_task.go`、`worker_run.go`、`worker_artifact.go`、`api/worker.js`、`store/workerRuntime.js`。 → 见 §3.4,建议收编为正式术语,成本为零。 ### 8.9 【P3】`businessApps` 疑似死代码 —— 建议删,不建议改名 - **定义**:`config/workbench.js:1434`,内容是**专员目录**(第一条即 `contract-review` 合同审查专员,带 `tier` / `workerType` / `roleCard`),名字与内容相反 - **引用情况**:模块外**零引用**;仅 `workbench.js` 内部 6 处自用(`2232` 按 legacy 路由查、`2236` 按 tier 筛、`2240` 数量、`2244` 可升级数、`2252-2253` dw/adw 统计) - **建议**:先确认那几个统计入口是否还有页面在用 → 没有就**整块删除**;有就随使用者一起迁到 `specialistCatalog` - **不要**改名成 `staticSpecialistCatalog` —— 那等于给一份没人读的静态副本续命 ### 8.10 【P3】`availableSkills` 静态 fallback `config/workbench.js:105`,22 条技能定义,被 4 处用于兜底:`store/skillCatalog.js:4`、`components/chat/PlusMenu.vue:183`、`config/projectTemplates.js:19`、`components/chat/CurrentObjectChip.vue:55`。 → 确认 `skillCatalog` 覆盖完整后,改名 `staticSkillCatalog`(语义准确),或一并删除。 ### 8.11 【已守住,勿回退】 - 后端对象 API 命名已清楚:`/api/specialists`、`/api/skills`、`/api/apps`、`/api/actions`(`router.go:43-50`) - 前端 catalog store 命名已规范:`specialistCatalog.js`(`normalizeSpecialist` / `getByKey` / `getByPath`)是标准范本 - 技能 key 前后端对齐测试已有(`model/skill_keys_test.go`,22 个 key) --- ## 九、反例库(真实事故) | 症状 | 根因 | 正确做法 | |---|---|---| | 从应用目录点进去,工作台是空的(专员/技能/提示词都没带上) | 写端读端各写一套 query 字面量,编译不报错 | 参数名提成共享常量(§7.4②) | | `const app = specialistCatalog.getByKey()` 读起来像在查应用 | 局部变量照抄了旧的"应用"心智 | 变量名必须与对象一致 | | `businessApps` 里装的全是专员 | 历史命名未随对象迁移 | 名实不符时优先删,其次改名 | | `capability_definition.go` 里装的是 skill 和 action | 中间态概念被固化成了文件名 | 让已经消失的概念从文件名里消失 | | `RoleKind` 字段名比模型名旧一代 | 模型改名了,字段没跟上 | 一套迁移要改到底,不能只改外层 | | 改了任务步骤的中文文案,UI 就永远显示"未执行" | `action_key` 由中文标题派生,名字成了协议 | 能不派生就不派生;非派生不可就加配对检查 | | `contract-review` 查出来两条不同对象的记录 | 同一字符串被两个对象共用 | 共名要登记 + 用测试守住 | | 差点把 ERP 的 `FCustId` "美化"成 `FCustId`→`CustID` | 误以为一致性高于一切 | 外部标准优先级最高,照抄 | | 文档里写 `file:///home/<用户名>/...` 链接 | 本机可点,他人全断 | 文档引用一律相对路径 | --- ## 十、修订记录 | 版本 | 日期 | 变更 | |---|---|---| | V1.0 | 2026-09-17 | 首版。原理 / 判据 / 术语表 / 分层规范 / 模式库 / 检查 / 修复 / 现状 / 反例库 | --- *注:本文件是命名问题的最高依据,与 `TOP_CODING_RULES.md` 配套使用。术语表(§3)变更属于架构级决策,改前先更新 `PROJECT_STATE.md` 的已拍板决策表。*