关于 consolidate-concepts
用于将多个 `.plain` 模块中的共享概念集中到一个导入模块的技能。它指导分析、规划和结构调整,要求先获明确批准,并保持功能、实现和测试要求不变。
主要功能
- 01梳理概念及其依赖
- 02规划共享导入模块
- 03更新导入与导出声明
- 04保持行为和各项要求不变
- 05验证引用及文件结构
适用场景
- 01消除模块间重复的概念定义
- 02简化冗长的导出概念列表
- 03模块拆分后共享领域定义
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ plainlang/ plain-forge --skill consolidate-concepts安装后在 Claude Code 中输入/consolidate-concepts确认已加载。
详细介绍
整合概念
请先加载规则。 在读取、编辑或编写任何 .plain 文件之前,调用 load-plain-reference 技能,并阅读它指引你查看的规则文件。只有在本次对话中已运行过 load-plain-reference 时,才可跳过此步骤。
使用时机
- 多个模块分别定义了相同或密切相关的概念。
requires链使用冗长的exported_concepts列表来共享定义,而改用导入会更简单。- 执行
refactor-module拆分后,共享概念出现重复或导出方式不够合理。 - 用户希望为多个模块使用的领域概念建立单一事实来源。
指导原则
这是一次结构性重构。所有受影响模块的行为必须保持不变。不得更改任何功能规格、实现要求或测试要求。只移动概念定义——从分散的位置移至一个共享导入模块,并更新各模块的 import 列表以引用该模块。
输入
需要整合概念的模块集合,以及可选的新(或现有)导入模块名称。如果未指定,请询问用户。
阶段 1 — 分析各模块中的概念
- 读取所有源模块。 对涉及的每个
.plain文件,读取完整文件,包括 frontmatter、定义、实现要求、测试要求和功能规格。同时读取其import和requires链。 - 建立概念清单。 对所有源模块中定义的每个概念,记录:
- 定义位置(文件及行号)
- 哪些模块引用了它(在定义、功能规格、实现要求或测试要求中)
- 它当前是否位于
exported_concepts列表中 - 它的定义引用了哪些其他概念(依赖关系图)
- 确定整合候选项。 满足以下任一条件的概念应予以整合:
- 在一个模块中定义,却被另一个模块中的规格引用(目前通过
exported_concepts共享) - 在多个模块中重复定义
- 属于基础领域概念,逻辑上应纳入共享词汇表
- 在一个模块中定义,却被另一个模块中的规格引用(目前通过
- 确定应保留在本地的概念。 满足以下任一条件的概念应保留在其所属模块中:
- 仅用于该模块自身的规格和定义
- 属于内部实现细节,在模块外没有意义
阶段 2 — 制定整合计划
向用户说明计划,并在进行更改前获得明确确认。
2a. 选择目标导入模块
- 新建导入模块 — 在
template/中创建新的.plain文件。选择一个能体现共享领域的名称(例如template/myapp-concepts.plain)。 - 现有导入模块 — 将概念添加到受影响模块已使用的导入模块中。这样可避免增加另一个导入引用。
如果目标是现有导入模块,请完整读取该文件,了解其中已有的内容。
2b. 确定要移动的概念
对于每个整合候选项,确认:
- 将从其当前模块的
***definitions***部分移除该概念。 - 将该概念添加到目标导入模块的
***definitions***部分。 - 将概念的完整定义文本(包括属性和约束的所有子弹项)原样移动。
2c. 确定目标中的概念顺序
导入模块中的概念必须按顺序排列,以确保每个概念都在其引用的所有概念之后定义。使用阶段 1 中的依赖关系图,确定有效的拓扑顺序。
2d. 确定导入和导出变更
对于每个受影响的模块,确定其 frontmatter 变更:
| 变更 | 适用情况 |
|---|---|
将新导入添加到 import 列表 | 模块尚未导入目标项 |
从 exported_concepts 中移除概念 | 这些概念仅为了共享定义而导出——现在由导入处理 |
在 exported_concepts 中保留概念 | 下游模块仍需通过导出获取这些概念(因为这些下游模块不会导入新模板) |
重要: import 会向导入该项的模块提供定义。requires 模块中的 exported_concepts 会在构建链中向下游模块提供概念。如果下游模块现在会直接 import 共享模板,就可以从 exported_concepts 中移除该概念。如果下游模块不导入该模板,则必须继续导出该概念。
2e. 提出计划
向用户总结:
- 目标导入模块(新建或现有)及其路径
- 要移动的概念列表,以及每个概念的来源模块
- 保留在本地的概念列表(以及原因)
- 各模块的 frontmatter 变更(新增
import项、从exported_concepts中移除的项目) - 确认不会更改任何功能规范、实现要求或测试要求
继续之前,必须获得用户的明确确认。
阶段 3 — 执行
3a. 创建或更新导入模块
如果要创建新的导入模块:
- 创建带有 YAML frontmatter 的
template/<name>.plain。如果它需要来自另一个导入项的概念,则将该项添加到自己的import列表中。 - 添加一个
***definitions***部分,按依赖顺序列出所有合并后的概念。 - 不要添加
***functional specs***—— 导入模块不得包含功能规范。
如果要更新现有导入模块:
- 读取当前文件。
- 将新概念插入
***definitions***部分,并按照相对于现有概念的依赖顺序排列。 - 确认没有与文件中已有概念或其自身导入项中的概念重名。
3b. 更新每个源模块
对于移出了概念的每个模块:
- 移除概念定义:从模块的
***definitions***部分移除概念定义,包括所有子项。 - 添加导入:将该导入项添加到模块 YAML frontmatter 的
import列表中(如果尚未添加)。 - 更新
exported_concepts:移除不再需要导出的概念(参见 2d)。 - 不要修改
***functional specs***、***implementation reqs***或***test reqs***—— 这些部分中的概念引用(:ConceptName:)保持不变,因为现在由导入提供该概念。
3c. 更新下游模块(如适用)
如果某个概念已从模块的 exported_concepts 中移除,而下游模块依赖该概念:
- 将新的导入项添加到下游模块的
import列表中,使其直接获取这些概念。 - 确认下游模块的规范仍可解析所有概念引用。
阶段 4 — 验证
4a. 概念可用性检查
对于每个受影响的模块(源模块、下游模块和导入模块本身):
- 功能规范中引用的每个
:Concept:都可解析(在本地定义、通过导入获取,或通过requires导出的概念获取) - 定义中引用的每个
:Concept:都在引用它的概念之前定义 - 实现要求和测试要求中引用的每个
:Concept:都可解析
4b. 重复项检查
- 同一概念未同时定义在导入模块和源模块中
- 同一概念未同时定义在导入模块和源模块使用的另一个导入项中
- 在每个模块的完整解析范围(本地 + 导入 +
requires导出项)内,概念名称全局唯一
4c. 定义完整性检查
- 每个迁移概念的定义文本都与原文完全一致——没有改写,也没有遗漏子条目
- 导入模块中的概念顺序符合依赖关系图(没有前向引用)
4d. 无行为变更检查
- 未修改任何模块中的
***functional specs*** - 未修改任何模块中的
***implementation reqs*** - 未修改任何模块中的
***test reqs*** - 未修改任何测试 plain 文件
4e. 结构验证
对于导入模块:
- 文件位于
template/目录中 - 不包含
***functional specs*** - 不使用
requires - YAML frontmatter 格式正确
对于每个源模块:
- YAML frontmatter 的
import列表包含目标导入模块 -
exported_concepts正确(没有过度导出,也没有遗漏导出) - 模块仍至少包含一条功能规格和一条实现要求
4f. 阅读所有已修改文件
完整阅读每个已修改的 .plain 文件,并将更改提交给用户审批。如果用户要求修改,请进行修改并重新验证。
常见陷阱
移动仅在本地使用的概念
如果某个概念仅在单个模块内被引用,将其整合到共享导入中会造成不必要的耦合。请保留在本地。
忘记更新下游模块
从 exported_concepts 中移除某个概念后,任何 requires 源模块且引用了该概念的模块都会出错——除非它们现在导入了共享模板。务必追踪完整的依赖链。
破坏概念顺序
导入模块中的概念必须按拓扑顺序排列。如果 :Meal: 在定义中引用了 :MealType:,那么 :MealType: 必须先出现。源模块中的局部顺序可能原本正确,但合并不同模块中的概念时,需要重新建立有效的顺序。
创建循环导入
导入模块不能 import 一个直接或间接反向导入它的模块。如果共享概念引用了现有模板中的概念,则新导入模块应导入该模板,而不是反过来。
过早从 exported_concepts 中移除概念
只有在所有需要该概念的下游模块现在都能通过 import 获取它时,才从 exported_concepts 中移除该概念。如果哪怕有一个下游模块没有导入共享模板,就必须保留该导出。
验证清单
- 用户在执行前确认了整合计划
- 所有迁移概念都在导入模块中定义,且文本完全一致
- 导入模块中的概念顺序符合依赖关系
- 导入模块和源模块中没有重复定义的概念
- 每个受影响模块的
import列表都包含目标导入模块 -
exported_concepts已正确更新——没有遗漏导出,也没有不必要的导出 - 所有受影响模块中的概念引用都能正确解析
- 未修改任何功能规格、实现要求、测试要求或测试 plain 文件
- 导入模块位于
template/中,且没有功能规格或 requires - 没有引入循环导入
- 必要时已更新下游模块
- 用户已批准最终结果