概念¶
SpexCode 围绕一棵 spec 节点 树来组织项目。一个节点是一份简短的文档,描述系统的某个部分应当做什么,旁边附带指向实现代码的指针。本页介绍这些节点如何组织、如何与代码关联,以及它们的状态从何而来。
spec 节点与节点树¶
一个 spec 节点是一个包含 spec.md 文件的目录。节点的 id 就是目录名。节点可以嵌套:子节点是父节点的子目录,因此 .spec/ 下的目录树就是节点树。把节点保留为目录而非单个文件,使它既能容纳自己的子节点,也能容纳就近放置的资源,而意图本身始终位于同一处。当两个节点的叶子名相同时,id 会用父路径中足够区分的一段来限定,以保持唯一。
把节点关联到代码¶
一个节点通过两个 frontmatter 列表声明它与源文件的关系。code: 指向该节点治理的文件,即它作为唯一真实来源(source of truth)的那一个文件。这是最紧密的关联:至多一个文件(治理多个是 lint 错误),漂移也挂在它上面。related: 列出该节点引用但并不拥有的文件;这些文件计入覆盖度,并携带一个更柔性的信号:当 related 文件领先于节点时,lint 警告但从不阻断。
多个节点可以治理同一个文件,这是普通的组合关系。工具只在一个文件被过多节点治理时才提示,把这读作该文件积累了过多职责的信号,而不是所有权本身出了问题。
节点主体:raw source 与 expanded spec¶
节点主体描述它当前的意图,并在原处改写。它不是变更日志,版本历史保存在 git 中,而不是在文件内不断堆积的段落里。
主体有两个带标签的部分,各有归属。Raw source 是人对意图的陈述:简短、很少改动,且只有在人工批准下才改。Expanded spec 是对代码应当做什么的更完整解读,是行为描述而非实现,只要与 raw source 保持一致就可以自由修订。这里刻意没有「当前状态」或「已完成」这类段落;进度是推导出来的,不是写下来的,因此主体无法叙述一个从未发生过的完成。
git 作为存储¶
这里没有单独的数据库。节点的可观察状态在读取时从 git 和工作区计算得出。它的 version 是改动过其 spec.md 的提交数量,纯重命名不计入。它的历史,即每个版本的原因、作者和 diff,来自这些相同的提交,通过每个提交携带的 trailer 归属。
状态是推导出来的¶
节点的状态是计算得来的,而非手工填写:pending(尚无实现代码的 spec)、active(正被某个进行中的改动触及的节点)、drift(被治理的代码已经领先于 spec 的最新版本)、merged(已提交且与代码同步)。frontmatter 中可以写一个状态,但对于有代码的节点,那只是 git 无法读取时的兜底;否则以推导结果为准。这就避免了一种常见的失败:文档因为有人写下「已完成」就声称某件事已完成。
查看方式¶
有两个界面渲染节点树及其历史:spex 命令行和一个 Web 仪表盘。两者读取同一份组装好的视图:节点树、每个节点推导出的状态与版本,以及进行中的改动。仪表盘是每次加载时从 git 重新计算的读取时视图,不是状态的第二份副本。