关于 break-down-func-spec
面向 `.plain` 规格编写者的技能,可将过于复杂的功能规格拆分为较小且有序的规格。它还会检查拆分结果是否保留原有行为,并适配模块上下文。
主要功能
- 01识别自然拆分边界
- 02保留所有已描述行为
- 03检查替换规格的复杂度
- 04批量分析与现有规格的冲突
- 05将测试场景迁移至替换规格
适用场景
- 01拆分被判定为过于复杂的规格
- 02添加前拆分捆绑在一起的行为
- 03替换规格后更新测试文件
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ plainlang/ plain-forge --skill break-down-func-spec安装后在 Claude Code 中输入/break-down-func-spec确认已加载。
详细介绍
拆分功能规格
先加载规则。 在读取、编辑或写入任何 .plain 文件之前,调用 load-plain-reference 技能,并阅读它指向的规则文件。只有在本次对话中已运行过 load-plain-reference 时,才可跳过此步骤。
适用场景
analyze-if-func-spec-too-complex将某个规格标记为 TOO COMPLEX。- 怀疑某个规格超过 200 LOC 限制,需要提前拆分。
- 某个规格包含多个应当分开的行为、构造或关注点。
输入
要拆分的功能规格,以及它所属的完整 .plain 文件。
工作流
- 阅读完整的
.plain文件 — 阅读定义、实现要求和所有功能规格(包括requires模块),以了解上下文。 - 确定要拆分的规格 — 即被标记为过于复杂的规格,或用户指定的规格。
- 分析规格过于复杂的原因 — 使用
analyze-if-func-spec-too-complex中的判断指标:- 是否捆绑了多个不同的行为?
- 是否一次引入了太多新构造?
- 是否包含复杂的分支逻辑或条件路径?
- 是否将跨领域关注点与核心功能混在一起?
- 是否在一个规格中描述了完整的 UI 屏幕?
- 是否涉及跨多个实体的复杂数据转换?
- 是否隐含着一个必须用代码构建的重要技术组件(引擎、解析器、调度器、算法、状态机、同步机制)?
- 确定拆分边界 — 找出可以自然拆分的部分。每个拆分后的规格都必须:
- 独立且有意义(结合前面的规格也能单独理解)
- 自包含(无需依赖后续规格即可发挥作用)
- 不超过 200 LOC 限制
- 起草替代规格 — 按时间顺序编写较小的规格,并根据
semantic-ids.md为每个规格设置自己的 func-id;保留原规格主要行为的替代规格可以沿用原 func-id。第一个替代规格通常负责搭建基础,后续规格则在此基础上逐步增加行为。 - 验证功能完整性 — 这一步至关重要。所有替代规格合在一起,必须涵盖原规格描述的 100% 功能。逐一检查原规格中的每种行为、条件和细节,并确认它们恰好出现在一个替代规格中。不得遗漏、弱化或含糊处理任何内容。如果原规格中的任何功能缺失,就将其补充到合适的替代规格中,或另建一个规格。
- 验证每个替代规格 — 对每个规格运行
analyze-if-func-spec-too-complex,确认其符合限制。 - 检查冲突 — 使用完整的替代规格集合,以及文件中所有现有规格和它们的
requires链,运行一次analyze-func-specs。批量分析器会在一次调用中报告所有冲突的规格对(替代规格 × 现有规格,以及替代规格 × 替代规格)——不要逐对循环检查。使用resolve-spec-conflict解决报告的每一对冲突,然后对已修改的规格集合重新运行analyze-func-specs,直到结果为COMPATIBLE。 - 替换文件中的规格 — 删除原有功能(标题和正文),并在其位置插入替代功能,每个功能都以
# <func-id>为标题并附上正文,同时保留时间顺序。 - 同步测试 plain 文件 — 对每个替代功能调用
add-test-scenarios。它会将原测试 plain 文件中的每个场景移至其所测试的替代功能,为尚无场景的结果编写测试场景,并在原 func-id 不再存在时删除原文件。 - 再次阅读文件,确认位置和语法正确。
拆分策略
策略 1:分离不同的行为
如果规格捆绑了多个可独立测试的操作,则将每个操作拆分成单独的规格。
之前:
***functional specs***
# manage-recipes
A :User: can create, edit, and delete :Recipe: items, with validation on all fields.
之后:
***functional specs***
# create-recipe
A :User: can create a :Recipe:. Only valid :Recipe: items can be created.
# edit-recipe
A :User: can edit an existing :Recipe:. Validation rules apply to the edited fields.
# delete-recipe
A :User: can delete a :Recipe:.
策略 2:将设置与行为分开
如果规范引入了一个新结构,并立即定义其复杂行为,请将其拆分为设置和行为。
之前:
***functional specs***
# meal-plan-screen
The :MealPlan: screen displays a weekly grid of :Slot: items, allows drag-and-drop reordering, and shows nutritional totals per day.
之后:
***functional specs***
# meal-plan-grid
The :MealPlan: screen displays a weekly grid of :Slot: items.
# reorder-slots
A :User: can reorder :Slot: items within a day on the :MealPlan: screen using drag-and-drop.
# daily-nutrition-totals
The :MealPlan: screen displays nutritional totals for each day.
策略 3:将核心逻辑与横切关注点分开
如果规范将主要功能与错误处理、重试、缓存、分页或日志记录混在一起,请将横切关注点提取到各自的规范中。
之前:
***functional specs***
# fetch-ingredients
:Ingredient: data is fetched from the external API with pagination, retry on transient errors, and caching for 10 minutes.
之后:
***functional specs***
# fetch-ingredients
:Ingredient: data is fetched from the external API.
# fetch-ingredients-paged
:Ingredient: data is fetched from the external API in pages.
# retry-ingredient-fetch
Fetching :Ingredient: data is retried on transient errors.
策略 4:将条件分支分开
如果规范描述了不同的模式或分支,请为每种模式或分支分别编写规范。
之前:
***functional specs***
# generate-meal-plan
:MealPlan: generation depends on :DietType:. Standard plans use round-robin assignment. Restrictive plans filter out excluded ingredients first, then apply round-robin. Custom plans allow manual slot-by-slot selection.
之后:
***functional specs***
# generate-standard-plan
A standard :MealPlan: is generated using round-robin :Recipe: assignment.
# generate-restrictive-plan
A restrictive :MealPlan: is generated.
- Excluded :Ingredient: items are filtered out first.
- Round-robin :Recipe: assignment is then applied.
# assign-custom-plan
A :User: can manually assign :Recipe: items to :Slot: items for a custom :MealPlan:.
策略 5:逐步构建 UI
如果规范描述了完整的屏幕,请将其拆分为布局和各个交互元素。
之前:
***functional specs***
# dashboard-screen
The :Dashboard: screen shows a summary card with stats, a scrollable list of recent :MealPlan: items, a floating action button to create a new plan, and a bottom navigation bar.
之后:
***functional specs***
# dashboard-summary-card
The :Dashboard: screen shows a summary card with :MealFrameStats:.
# dashboard-recent-plans
The :Dashboard: screen shows a scrollable list of recent :MealPlan: items.
# dashboard-new-plan-button
The :Dashboard: screen includes a button to create a new :MealPlan:.
策略 6:提取可复用的技术组件
有时,规范之所以过于复杂,并不是因为它打包了许多行为,而是因为它隐含要求在规范中直接构建一个重要的技术组件,例如引擎、解析器、调度器、布局算法、状态机或同步机制。当复杂性集中在单个组件上时,横向拆分行为并无帮助。此时应将组件提取出来:为它定义一个概念,在链条中更早的位置用专门的规范构建它,然后改写原规范,通过引用该组件来使用它。由于规范会按从上到下的顺序逐步生成,原规范现在所隐含的代码会少得多——它只需连接一个已存在的组件,而不必从头实现。
在第一个引用该组件的规范之前,通过 add-concept 在 ***definitions*** 中定义组件概念。
之前:
***functional specs***
# export-report-pdf
A :Report: is exported to PDF, laying out multi-page tables with repeating headers, page numbers, and charts rendered from :Report: data.
之后:
***functional specs***
# pdf-renderer-tables
:PdfRenderer: lays out multi-page tables from structured content.
- Table headers repeat at the top of each page.
- Each page shows its page number.
# export-report-pdf
A :Report: is exported to PDF using :PdfRenderer:, embedding charts rendered from :Report: data.
提取出的组件规范与改写后的原规范必须共同覆盖原始功能的 100%(步骤 6):原规范必须引用该组件,绝不能重新描述它。将组件的技术和架构选择放在 ***implementation reqs*** 中,而不是功能规范中。如果组件是独立的子系统,或会在多个模块间复用,请使用 create-requires-module / refactor-module 将其提升为独立模块,而不是同级规范。
保留时间顺序
替代规范占据原规范的位置。较早的规范保持不变。第一个替代规范应仅根据其上方的规范就能成立。后续每个替代规范都可以引用此前规范中的行为。
原功能的测试纯文本文件也应以相同方式拆分:每个场景都移至它所测试的替代功能中;不再适用于任何一个替代项的场景应重写;启用一致性测试时,每个替代项都应至少以一个一致性场景结尾(参见 add-test-scenarios)。
验证清单
- 已从文件中移除原始规范
- 替换规范合计覆盖原始规范的 100% 功能,没有任何遗漏
- 每项替换规范所隐含的代码量均 ≤ 200 LOC(已通过
analyze-if-func-spec-too-complex验证) - 替换规范与现有规范之间不存在冲突(已通过一次批量
analyze-func-specs调用验证) - 替换规范彼此之间不存在冲突(由同一次批量调用检查)
- 替换规范的时间顺序正确
- 每项替换规范都具有独立意义
- 替换规范中引用的所有
:Concepts:均已定义 - 提取出的任何技术组件均已定义为概念,由其自身更早的规范构建,并且原始规范只对其进行引用(而非重新描述)
- 替换规范与语言无关
- 所有规范均已按照
concise-specs.md精简——拆分不是重新添加说明文字的许可 - 所有外部接口均保持明确
- 测试纯文本文件遵循拆分结果:每项替换功能对应一个文件,移动或重写每个原始场景,且没有任何场景绑定到已不存在的 func-id