跳转至

概念

SpexCode 围绕一棵 spec 节点 树来组织项目。一个节点就是一份简短的文档,描述系统的某个部分应当做什么,并在旁边附带一个指向实现代码的指针。本页介绍这些节点如何组织、如何与代码关联,以及它们的状态从何而来。

spec 节点与节点树

一个 spec 节点是一个包含名为 spec.md 文件的目录。节点的 id 就是目录名。节点可以嵌套:子节点是父节点的子目录,因此 .spec/ 下的目录树就是节点树。把节点保留为目录而非单个文件,使它既能容纳自己的子节点,也能容纳任何就近放置的资源,而意图本身始终位于同一处。当两个节点的叶子名相同时,id 会用父路径中足够区分的一段来限定,以保持唯一。

把节点关联到代码

一个节点通过两个 frontmatter 列表来声明它与源文件的关系。code: 列出该节点 治理 的文件——即它作为唯一真实来源(source of truth)的那些文件。这是最紧密的关联,理想情况下只指向单个文件,drift(漂移) 也正是挂在它上面。related: 列出该节点引用但并不拥有的文件;这些文件计入覆盖度,但不承载所有权。

多个节点可以治理同一个文件,这是普通的组合关系。工具只有在一个文件被过多节点治理时才会提示,把这读作该文件积累了过多职责的信号,而不是所有权本身出了问题。

节点主体:raw source 与 expanded spec

节点主体描述它 当前 的意图,并在原处改写。它不是变更日志——版本历史保存在 git 中,而不是在文件内不断堆积的段落里。

主体有两个带标签的部分,各有不同的归属。Raw source 是人对意图的陈述:简短、很少改动,且只有在人工批准下才改。Expanded spec 是对代码应当做什么的更完整解读——是行为描述,而非实现——只要与 raw source 保持一致,就可以自由修订。这里刻意没有“当前状态”或“已完成”这类段落;进度是推导出来的,而不是写下来的,因此主体无法叙述一个从未发生过的完成。

git 作为存储

这里没有单独的数据库。节点的可观察状态是在读取时从 git 和工作区计算得出的。它的 version 是改动过其 spec.md 的提交数量,纯重命名不计入。它的历史——每个版本的原因、作者和 diff——来自这些相同的提交,并通过每个提交携带的 trailer 归属。

状态是推导出来的

节点的状态是计算得来的,而非手工填写:pending(尚无实现代码的 spec)、active(正被某个进行中的改动触及的节点)、drifted(被治理的代码已经领先于 spec 的最新版本,即发生了 drift)、或 merged(已提交且与代码同步)。frontmatter 中可以写一个状态,但对于有代码的节点,那只是在 git 无法读取时的兜底;否则以推导结果为准。这就避免了一种常见的失败:文档因为有人写下“已完成”就声称某件事已完成。

查看方式

有两个界面渲染节点树及其历史:spex 命令行和一个 Web 仪表盘。两者读取的是同一份组装好的视图——节点树、每个节点推导出的状态与版本,以及任何进行中的改动。仪表盘是一个在每次加载时从 git 重新计算的读取时视图,而不是状态的第二份副本。