日期:2026-04-02 作者:Codex(自动分析生成)
你提到 “最近对 SDD 进行了大更新”,并希望看到:
- 使用 真实
openspec/specs/**与openspec/changes/**的例子(内容要“比较长”) - 基于这些工件,逻辑推理出 SDD 的“风格处理”(
ison / toon / yaml)应如何工作 - 输出一个可阅读的分析报告,并给出 token 量的推断
本报告以仓库内的真实工件为输入,给出:
- 从 OpenSpec spec/changes 抽象出的语义 IR(中间表示)
- 三种风格(
ison/toon/yaml)的 envelope 约束、解析/写回策略、转换(convert)门禁 - 一组 等语义的 main spec 与 delta spec(变化 spec)在三种风格下的示例
- 对上述示例的 token 粗估(含方法与误差说明)
选用归档 change:openspec/changes/archive/2026-04-01-support-multi-style-sdd-specs/
openspec/changes/archive/2026-04-01-support-multi-style-sdd-specs/proposal.mdopenspec/changes/archive/2026-04-01-support-multi-style-sdd-specs/design.mdopenspec/changes/archive/2026-04-01-support-multi-style-sdd-specs/tasks.md
这组文档非常典型:它同时给出 为什么做、设计约束、实现拆分与测试点,足以推导出多风格处理的“必须正确工作”的逻辑。
为了把“风格处理”讲清楚,报告使用了两份 spec:
-
多风格行为约束(核心)
openspec/specs/sdd-multi-style-formats/spec.md -
三风格的 canonical 结构契约(定义主 spec/delta spec 的结构)
openspec/specs/sdd-ison-authoring/spec.md
说明:你原话希望“用一个真实 spec”,但在 SDD 多风格场景里,
sdd-multi-style-formats负责“风格门禁/转换/语义一致性”,而sdd-ison-authoring负责“canonical payload 的结构契约”。两者一起才能完整推导出“解析/验证/写回/转换”的闭环;报告会明确区分它们的作用。
support-multi-style-sdd-specs 的设计核心(见 design.md)是:
- 风格差异只停留在 envelope(fence + 语法)层
- 命令(
show/list/validate/archive、authoring helpers、convert)应消费 同一个语义模型,避免复制三套业务逻辑
这意味着系统必然需要一个 style-agnostic 的 IR(中间表示),并且三种风格都必须可逆地映射到它。
kind(必须为llman.sdd.spec)name(strict 模式下必须等于<capability>)purposerequirements[]:每项含req_id/title/statementscenarios[]:每项含req_id/id/given/when/then
kind(必须为llman.sdd.delta)ops[]:每项含op/req_id/title/statement/from/to/nameop_scenarios[]:每项含req_id/id/given/when/then
这两份 IR 的字段集合,与
openspec/specs/sdd-multi-style-formats/spec.md“三种风格必须共享同一语义模型” 部分一致。
这里把“风格处理”拆成 4 个层次:配置门禁 → envelope 识别 → 语义解析/校验 → 写回/转换。
从 openspec/specs/sdd-multi-style-formats/spec.md 与 ...support-multi-style.../design.md 可以推出以下硬门禁:
llmanspec/config.yaml必须显式声明:spec_style: ison|toon|yaml- 对 “读取或改写 spec/delta payload” 的命令:
- 缺失 / 为空 / 非法
spec_style→ 直接失败(不允许“默认 ison 继续”)
- 缺失 / 为空 / 非法
- 已声明
spec_style的项目:- 遇到不同风格 fence / payload → 直接失败,并给出 expected vs found
这保证了:项目风格是单一的、可审计的,避免“同仓库混用多语法”导致的非确定性。
从 sdd-multi-style-formats 得到 “风格→fence” 对应关系:
ison→```isontoon→```toonyaml→```yaml
并推出 envelope 处理策略:
- 从
spec.md(Markdown)中提取 fenced blocks - 严格检查“出现了哪些 fence”:
- 若项目风格为
yaml,但文件只含```ison→ fail(明确指出 mismatch)
- 若项目风格为
- 若风格为
ison:- 允许多个
```isonblock(sdd-ison-authoring明确要求支持多 block 合并)
- 允许多个
- 若风格为
toon/yaml:- 必须是一个 canonical 文档(单 fence),避免引入风格专属的 block merge 规则
由 sdd-ison-authoring 可以推出两层校验:
-
Envelope 校验(风格相关)
- fence 类型正确
- 对
ison:canonical block 名称、列名、重复 block 等错误
-
IR 校验(风格无关)
kind正确(spec/delta)- strict:
name == capability - 每个 requirement ≥ 1 个 scenario
(req_id, id)唯一- delta op 的规则(
add/modify/remove/rename对字段的约束)
关键点:只有 envelope 部分依赖风格,IR 校验必须完全一致,这样 show/list/validate/archive 才能跨风格等价。
从 support-multi-style.../proposal.md 与 design.md 的 YAML 段落可推出:
ison:默认 token-friendly dump(不做列对齐),可选 pretty 对齐(仅限 ison)toon:稳定、严格的 canonical 编码(固定顺序、固定结构)yaml:- 优先尝试 lossless overlay(保留注释/格式/键顺序尽量不动)
- overlay 应以语义 ID(如
req_id、(req_id,id))为锚点生成 patch 计划 - overlay 失败 → 回退为“仅重写 fenced YAML payload 的确定性重写”(Markdown 外围保留,但 payload 内注释可能丢失)
这解释了为什么 YAML 后端比 ISON/TOON 更复杂:它承担了“人类可维护 YAML spec”的编辑体验目标。
为了对齐你想看的 “不同形态”,下面用 sdd-multi-style-formats 这份真实 capability spec 的语义,构造一份 等语义 main spec,并用 change support-multi-style-sdd-specs 构造一份 delta spec 示例(表示在 sdd-workflow 里新增了两条要求:风格门禁 + convert)。
注意:
req_id/scenario id在 OpenSpec Markdown 里并不是强制字段,本报告为了演示 SDD canonical payload,给这些 requirement/scenario 人工分配了稳定 ID(这也是 SDD 结构化写法的一个关键收益:可引用、可 patch、可验证)。
在 SDD 工作流里,main spec 通常带 frontmatter(见 openspec/specs/sdd-workflow/spec.md 的 “Spec 校验元数据” 要求)。三种风格都共用同一套 wrapper;只有 fenced payload 不同。
---
llman_spec_valid_scope:
- src
llman_spec_valid_commands:
- just test
llman_spec_evidence:
- tests/sdd_integration_tests.rs
---
# sdd-multi-style-formatsobject.spec
kind name purpose
"llman.sdd.spec" sdd-multi-style-formats "项目级显式选择 ison/toon/yaml,并保持严格风格门禁。"
table.requirements
req_id title statement
r1 "显式 spec_style" "`llmanspec/config.yaml` MUST 显式声明 `spec_style` 且值 MUST 为 `ison|toon|yaml`;缺失/非法 MUST 失败并提示修复。"
r2 "风格严格匹配" "主 spec 与 delta spec 的 canonical payload fence MUST 与项目 `spec_style` 严格一致;不得自动探测/回退。"
r3 "统一语义模型" "无论 `ison|toon|yaml`,解析后 MUST 归一化到同一语义 IR,再驱动 show/list/validate/archive。"
r4 "显式 convert" "系统 MUST 提供显式风格转换(项目/单文件),并在写入前后重解析验证语义等价;失败 MUST 不更新配置。"
r5 "标记 experimental" "`toon` 与 `yaml` MUST 在帮助/错误/模板中标记为 experimental。"
table.scenarios
req_id id given when then
r1 init_has_style "" "运行 `llman sdd init`" "生成的 `llmanspec/config.yaml` 包含 `spec_style: ison`"
r1 missing_style_blocks "" "项目缺失 `spec_style` 且运行 `llman sdd show sample --type spec`" "命令失败并提示先设置 `spec_style`"
r2 toon_rejects_ison "" "项目声明 `spec_style: toon` 但 spec 文件只有 ` ```ison `" "validate 失败并指出 expected=toon found=ison"
r2 mixed_styles "" "主 spec 使用 `yaml` 且某个 delta spec 使用 `toon`" "validate --changes 失败并指出不允许混用"
r3 show_semantic_equal "" "同一语义分别以 ison/yaml/toon 编写" "show --json 的语义字段一致"
r3 archive_semantic_equal "" "同一语义分别以 ison/yaml 编写并归档" "archive merge 后语义结果一致"
r4 convert_project_success "" "执行项目范围 convert 并全部重解析通过" "文件被重写且最后更新 `llmanspec/config.yaml`"
r4 convert_project_fail "" "转换后重解析某文件失败" "命令失败且 `llmanspec/config.yaml` 保持旧值"
r5 help_marks_experimental "" "查看帮助/模板示例" "明确标注 toon/yaml 为 experimental"
- canonical block 名称必须固定:
object.spec/table.requirements/table.scenarios - 允许把三个 block 分散在多个
```isonfence 中(按 block name 合并),但 同名 block 重复即错误 - 默认 dump 要 token-friendly:不做列对齐填充(否则 padding 会膨胀 token)
kind: llman.sdd.spec
name: sdd-multi-style-formats
purpose: '项目级显式选择 ison/toon/yaml,并保持严格风格门禁。'
requirements:
- req_id: r1
title: 显式 spec_style
statement: llmanspec/config.yaml MUST 显式声明 spec_style 且值 MUST 为 ison|toon|yaml;缺失/非法 MUST 失败并提示修复。
- req_id: r2
title: 风格严格匹配
statement: 主 spec 与 delta spec 的 canonical payload fence MUST 与项目 spec_style 严格一致;不得自动探测/回退。
- req_id: r3
title: 统一语义模型
statement: 无论 ison|toon|yaml,解析后 MUST 归一化到同一语义 IR,再驱动 show/list/validate/archive。
- req_id: r4
title: 显式 convert
statement: 系统 MUST 提供显式风格转换(项目/单文件),并在写入前后重解析验证语义等价;失败 MUST 不更新配置。
- req_id: r5
title: 标记 experimental
statement: toon 与 yaml MUST 在帮助/错误/模板中标记为 experimental。
scenarios:
- req_id: r1
id: init_has_style
given: ''
when: 运行 llman sdd init
then: 生成的 llmanspec/config.yaml 包含 spec_style: ison
- req_id: r1
id: missing_style_blocks
given: ''
when: 项目缺失 spec_style 且运行 llman sdd show sample --type spec
then: 命令失败并提示先设置 spec_style
- req_id: r2
id: toon_rejects_ison
given: ''
when: 项目声明 spec_style: toon 但 spec 文件只有 ```ison
then: validate 失败并指出 expected=toon found=ison
- req_id: r2
id: mixed_styles
given: ''
when: 主 spec 使用 yaml 且某个 delta spec 使用 toon
then: validate --changes 失败并指出不允许混用
- req_id: r3
id: show_semantic_equal
given: ''
when: 同一语义分别以 ison/yaml/toon 编写
then: show --json 的语义字段一致
- req_id: r3
id: archive_semantic_equal
given: ''
when: 同一语义分别以 ison/yaml 编写并归档
then: archive merge 后语义结果一致
- req_id: r4
id: convert_project_success
given: ''
when: 执行项目范围 convert 并全部重解析通过
then: 文件被重写且最后更新 llmanspec/config.yaml
- req_id: r4
id: convert_project_fail
given: ''
when: 转换后重解析某文件失败
then: 命令失败且 llmanspec/config.yaml 保持旧值
- req_id: r5
id: help_marks_experimental
given: ''
when: 查看帮助/模板示例
then: 明确标注 toon/yaml 为 experimental- 解析:把 fenced YAML 解析到 IR(字段必须齐全、顺序不影响语义)
- 写回:优先 lossless overlay(语义锚点:
req_id、(req_id,id));失败再 fallback 到 deterministic rewrite
kind: llman.sdd.spec
name: sdd-multi-style-formats
purpose: "项目级显式选择 ison/toon/yaml,并保持严格风格门禁。"
requirements[5]{req_id,title,statement}:
r1,"显式 spec_style","llmanspec/config.yaml MUST 显式声明 spec_style 且值 MUST 为 ison|toon|yaml;缺失/非法 MUST 失败并提示修复。"
r2,"风格严格匹配","主 spec 与 delta spec 的 canonical payload fence MUST 与项目 spec_style 严格一致;不得自动探测/回退。"
r3,"统一语义模型","无论 ison|toon|yaml,解析后 MUST 归一化到同一语义 IR,再驱动 show/list/validate/archive。"
r4,"显式 convert","系统 MUST 提供显式风格转换(项目/单文件),并在写入前后重解析验证语义等价;失败 MUST 不更新配置。"
r5,"标记 experimental","toon 与 yaml MUST 在帮助/错误/模板中标记为 experimental。"
scenarios[9]{req_id,id,given,when,then}:
r1,init_has_style,"","运行 llman sdd init","生成的 llmanspec/config.yaml 包含 spec_style: ison"
r1,missing_style_blocks,"","项目缺失 spec_style 且运行 llman sdd show sample --type spec","命令失败并提示先设置 spec_style"
r2,toon_rejects_ison,"","项目声明 spec_style: toon 但 spec 文件只有 ```ison","validate 失败并指出 expected=toon found=ison"
r2,mixed_styles,"","主 spec 使用 yaml 且某个 delta spec 使用 toon","validate --changes 失败并指出不允许混用"
r3,show_semantic_equal,"","同一语义分别以 ison/yaml/toon 编写","show --json 的语义字段一致"
r3,archive_semantic_equal,"","同一语义分别以 ison/yaml 编写并归档","archive merge 后语义结果一致"
r4,convert_project_success,"","执行项目范围 convert 并全部重解析通过","文件被重写且最后更新 llmanspec/config.yaml"
r4,convert_project_fail,"","转换后重解析某文件失败","命令失败且 llmanspec/config.yaml 保持旧值"
r5,help_marks_experimental,"","查看帮助/模板示例","明确标注 toon/yaml 为 experimental"
- 单 fence 单文档,避免 TOON 引入“多 block 合并规则”
- 稳定序列化:字段顺序、数组顺序确定(否则 diff 噪音会很大)
下面给出一个 delta spec 的“变化语义”示例:假设 change support-multi-style-sdd-specs 要在 sdd-workflow 里新增两条 requirement:
style_gate:要求 spec 相关命令必须显式spec_style(缺失/非法直接失败)convert:要求提供llman sdd convert并验证语义等价
这与
openspec/changes/archive/2026-04-01-support-multi-style-sdd-specs/design.md的核心约束一致:strict gating + explicit convert。
object.delta
kind
"llman.sdd.delta"
table.ops
op req_id title statement from to name
add_requirement style_gate "Spec style gating" "Spec read/write commands MUST require explicit `spec_style`; missing/invalid MUST fail with a concrete hint." ~ ~ ~
add_requirement convert "Explicit convert" "System MUST provide `llman sdd convert` for audited migration between `ison|toon|yaml` and MUST verify semantic equivalence." ~ ~ ~
table.op_scenarios
req_id id given when then
style_gate missing_config "" "user runs `llman sdd show` without configured `spec_style`" "command fails and explains how to set `spec_style`"
convert project_success "" "user runs `llman sdd convert --to yaml --project` and all files reparse" "converted files are written and config is updated last"
kind: llman.sdd.delta
ops:
- op: add_requirement
req_id: style_gate
title: Spec style gating
statement: Spec read/write commands MUST require explicit spec_style; missing/invalid MUST fail with a concrete hint.
from: null
to: null
name: null
- op: add_requirement
req_id: convert
title: Explicit convert
statement: System MUST provide llman sdd convert for audited migration between ison|toon|yaml and MUST verify semantic equivalence.
from: null
to: null
name: null
op_scenarios:
- req_id: style_gate
id: missing_config
given: ''
when: user runs llman sdd show without configured spec_style
then: command fails and explains how to set spec_style
- req_id: convert
id: project_success
given: ''
when: user runs llman sdd convert --to yaml --project and all files reparse
then: converted files are written and config is updated lastkind: llman.sdd.delta
ops[2]{op,req_id,title,statement,from,to,name}:
add_requirement,style_gate,"Spec style gating","Spec read/write commands MUST require explicit spec_style; missing/invalid MUST fail with a concrete hint.",null,null,null
add_requirement,convert,"Explicit convert","System MUST provide llman sdd convert for audited migration between ison|toon|yaml and MUST verify semantic equivalence.",null,null,null
op_scenarios[2]{req_id,id,given,when,then}:
style_gate,missing_config,"","user runs llman sdd show without configured spec_style","command fails and explains how to set spec_style"
convert,project_success,"","user runs llman sdd convert --to yaml --project and all files reparse","converted files are written and config is updated last"
不同模型使用不同 tokenizer;在不引入模型专用 tokenizer 的前提下,本报告采用启发式估算:
- ASCII 字符按 ~4 chars/token
- CJK 与其他非 ASCII 字符按 ~1 char/token
- 不计空白字符(因此对
pretty对齐类输出会有低估)
所以结果应按 ±20% 的误差带来理解:用来比较风格之间的相对大小是可靠的,用来做严格预算不可靠。
对 4.2~4.5 中的示例文本做估算,结果如下(单位:tokens,越小越省):
| 文档 | ison | yaml | toon |
|---|---|---|---|
| main spec(仅 payload) | ~548 | ~600 | ~549 |
| main spec(含 frontmatter+标题) | ~584 | ~636 | ~584 |
| delta spec(仅 payload) | ~169 | ~180 | ~174 |
结论(对这个示例):
yaml因为重复 key(req_id/title/...)带来额外结构开销,token 更大ison与toon在“表格/行式结构”上更接近,token 更省且更稳定- 当 requirements/scenarios 数量继续增长时,
yaml的结构开销通常增长更快
结合 support-multi-style-sdd-specs 的目标(strict gating、统一 IR、确定性写回、显式 convert)和上面的 token 粗估:
-
默认推荐:
ison- 优点:最贴合 SDD 的 row-level 编辑、最 token-friendly、允许多 block 分段组织(但仍然保持 canonical 结构)
- 风险:需要遵守 canonical block/列名;旧的 JSON-in-ison 会被拒绝(这是刻意的“强收敛”)
-
想要更直读:
yaml(experimental)- 优点:对人类更直观(尤其是新手)
- 代价:token 更大;写回要做 overlay 以保留注释/格式,复杂度更高
-
想要更紧凑但又结构化:
toon(experimental)- 优点:紧凑、结构化、比 YAML 更少结构冗余
- 代价:语法更小众;需要强约束 canonical emitter 才能避免 diff 噪音
本报告已经给了:
- 真实 openspec change + specs 的约束提炼
- 一套等语义三风格 main/delta spec 示例
- token 粗估
如果你想进一步对比“更长、更接近真实仓库的 spec”,我可以把 openspec/specs/sdd-workflow/spec.md 中某个更大的子集(比如 init/update/validate/archive 一整段)抽取成 IR,并生成三风格 canonical payload,再给出 token 对比表(会明显更长、更有冲击力)。