关于 add-functional-specs
add-functional-specs 将单条规格编写流程用于按顺序排列的多条规格。每条规格插入前都会经过复杂度和冲突检查,之后可添加一致性测试。
主要功能
- 01按依赖顺序起草规格
- 02逐条检查复杂度
- 03与已有规格批量分析冲突
- 04逐条插入规格
- 05为每条规格添加测试
适用场景
- 01编写多个相关功能的规格
- 02批量编写并逐条完成检查
- 03添加有序规格及一致性测试
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ plainlang/ plain-forge --skill add-functional-specs安装后在 Claude Code 中输入/add-functional-specs确认已加载。
详细介绍
添加功能规格
先加载规则。 在阅读、编辑或编写任何 .plain 文件之前,调用 load-plain-reference 技能,并阅读它指向的规则文件。仅当本次对话中已运行过 load-plain-reference 时,才可跳过此步骤。
适用场景
- 用户要求一次性添加多项功能规格(例如“添加这三项规格”“用几条要点描述此功能的规格”)。
- 更高级别的技能或工作流(
forge-plain、add-feature、debug-specs)需要编写一组紧密相关的规格。 - 任何情况下,若你本来打算在不逐项分析的情况下编写多项功能规格。
如果只需添加一项规格,请改用 add-functional-spec 技能。无论使用哪种技能,都不得手动编辑 ***functional specs*** 部分——每个新条目都必须通过这两种技能之一添加,以确保实际执行复杂度和冲突检查。
工作流
此技能是按顺序逐项应用 add-functional-spec,不得走捷径。批量操作也必须严格逐项执行。
- 确定目标
.plain文件。 如果目标不明确,请询问用户。 - 阅读整个文件,了解现有定义、实现要求和所有当前功能规格(包括通过
import/requires引入的规格)。 - 拟定所有准备添加的规格,按应出现的时间顺序排列,并按照
semantic-ids.md为每项分配自己的 func-id;该 ID 在模块内以及本批次内都必须唯一。将草稿保存在工作列表中,暂不插入任何内容。继续之前,按照concise-specs.md对每份草稿进行审阅;在保留所有事实的前提下,将每份内容精简至最短。 - 按顺序处理每份草稿规格:
-
分析复杂度。 对该规格运行
analyze-if-func-spec-too-complex。如果结论为TOO COMPLEX,运行break-down-func-spec,用拆分后的小规格替换工作列表中的条目(保持顺序),然后从第一份拆分后的规格重新执行此步骤。 -
检查冲突。 使用以下单一输入集运行一次
analyze-func-specs:- 新规格,以及
- 文件中所有现有功能规格及其
requires链中的规格,以及 - 本批次中已插入的所有规格。
批量分析器会在一次调用中返回所有冲突的规格对——不要循环调用逐对分析器。对于它报告的每一对冲突,运行
resolve-spec-conflict。应用解决方案——可能需要修改新规格、现有规格,或两者——然后针对已修改的规格集重新运行一次analyze-func-specs,直至结论为COMPATIBLE。 -
追加规格。 将规格以
# <func-id>标题及其正文的形式,添加到***functional specs***部分。规格按时间顺序排列;新条目放在末尾(除非有某项较早的现有规格必须排在它之后——这种情况很少见,此时应插入正确位置)。
-
- 再次阅读文件,确认整批规格的位置、顺序和语法均正确。
- 编写测试 plain 文件。 如果该模块启用了符合性测试,则针对每项已插入的功能,按顺序调用一次
add-test-scenarios。
顺序很重要:每项新规格只有在通过复杂度和冲突检查后才能插入,因此批次中的后续规格始终会基于已验证的文件进行检查。
规则
add-functional-spec 的所有规则都适用于每项规格。为使此技能内容完整,现再次列出这些规则。
复杂度上限(每项规格)
每项功能规格必须确保最多改动 200 行代码。如果规格规模过大,请使用 break-down-func-spec 将其拆分为多个更小且相互独立的规格。不要在规格文本中加入代码行数估算。
按时间顺序排序
规格会从上到下逐步呈现。渲染器不了解后续规格——上下文中只有此前已呈现的规格。
- 新规格可以引用此前规格中定义的行为。
- 新规格不能假设后续规格中的任何内容。
- 排列这一批规格,使每个规格仅依赖排在它前面的规格。如果两个新规格会相互引用,则依赖其他规格的那个排在后面。
语言无关性
描述行为、概念和领域逻辑,而不是实现结构。避免使用特定语言的类型、泛型语法、框架注解,或其他与特定语言或框架绑定的结构。通用技术术语(空值、JSON 类型、HTTP 状态码)可以使用。特定语言的指导应放在 ***implementation reqs*** 中。
不得冲突(每个规格通过一次批量调用实现全面覆盖)
新规格之间不得相互矛盾,也不得与任何现有功能规格矛盾。规格冲突是代价最高的结果。冲突检查必须覆盖:
- 每一组(新规格 × 现有规格)配对;以及
- 批次内每一组(新规格 × 新规格)配对。
通过对每个待插入规格各运行一次 analyze-func-specs 来实现上述覆盖:将新规格、所有现有规格以及本批次中所有已插入的规格作为一个集合传入。批量分析器会在一次调用中列出所有冲突配对——不要逐对调用分析器。如果存在歧义,请添加明确的细节,以消除所有可能的冲突解释。对于批量分析器报告的每一组冲突配对,在继续之前都使用 resolve-spec-conflict 技能进行诊断并修复。
消除歧义(每个规格)
每个功能规格都必须明确无歧义——渲染器只能有一种合理的解读。如果陈述本身不足以完全明确行为,请在其下添加 - 项目或句子。这些细节用于阐明陈述,不会引入独立功能。即使添加了这些细节,规格所暗示的代码量仍必须 ≤ 200 行。
***functional specs***
# send-message
A :User: can send a :Message: to a :Conversation:.
- A :Message: must have non-empty content.
- The :Message: is appended to the end of the :Conversation:.
- All :Participant: members of the :Conversation: can see the new :Message:.
确定性接口
规格必须足够详细,让开发者无需阅读生成的代码就能使用已构建的软件。必须明确规定所有外部接口——REST 端点路径和 HTTP 方法、CLI 命令名称和参数、文件格式、消息架构等。绝不能将接口细节留给渲染器自行决定。
封装
功能必须在规格文本中自包含。requires 模块只会导入功能规格,因此不要依赖实现需求来传达行为——下游模块看不到这些内容。
测试场景
插入完这一批规格后,按顺序对每项已插入的功能调用一次 add-test-scenarios。该工具会为每项功能写入测试纯文本文件(按惯例为 tests/<module>/<func-id>.plain);当模块启用一致性测试时,该文件应至少包含一个一致性测试场景。每个规格都要单独执行此操作,不要将其作为批量处理的一部分。
反模式
- 不要因为批次“看起来”很小就跳过逐项规范检查。 即使只有两项规范,也需要进行两次复杂度检查,并执行一次包含两项新规范的批量冲突检查。
- 不要退回到逐对分析冲突。 每插入一项规范,就使用完整的相关集合调用一次
analyze-func-specs,而不是对每一对都调用analyze-2-func-specs。 - 不要先完成所有复杂度检查,最后再做所有冲突检查。 按工作流中的说明逐项交替执行;否则,前面的复杂度检查若未通过,就会浪费后续的冲突检查。
- 不要先插入所有规范,然后再进行分析。 不合格的规范一旦写入文件,就会影响后续的冲突分析。
- 不要因为要添加的规范“很多”就改为手动编写。 正确做法是增加分析器调用次数,而不是减少。
- 不要将此技能用于单项规范。 请使用
add-functional-spec;它包含相同的检查,但无需批量处理记录。
验证清单
- 批次中的每项规范都意味着代码变更不超过 200 行(通过
analyze-if-func-spec-too-complex验证) - 每项新规范都与任何现有规范不冲突(通过批量调用
analyze-func-specs验证) - 批次中的任意两项新规范都不冲突(由相同的批量
analyze-func-specs调用检查) - 批量分析器报告的每一对冲突都在批次完成前通过
resolve-spec-conflict解决 - 所有规范均使用与语言无关的表述(不涉及特定语言或框架)
- 所有外部接口均明确(端点路径、方法、CLI 参数、格式等)
- 所有引用的
:Concepts:均在***definitions***中定义 - 句子简短、清晰且明确
- 每项规范均按照
concise-specs.md精简:不写显而易见的内容,不写理由,不使用无法验证的形容词或模糊措辞,全程使用直白的词语 - 不与现有规范或批次内的其他规范重复
- 规范均放置在正确的时间顺序位置
- 每个 func-id 均遵循
semantic-ids.md,且在模块内和批次内唯一 - 启用一致性测试时,已为每项插入的功能调用
add-test-scenarios