Spec Kit 项目宪法深度解析:specify-cli 的五大治理原则与质量门禁
Spec Kit 仓库中的 .specify/memory/constitution.md 是为 specify-cli 代码库正式批准的 v1.0.0 项目宪法(批准日期 2026-06-19)。它不是空泛的口号文档,而是从源码、测试套件、CI 流水线与项目约定(AGENTS.md、CONTRIBUTING.md、DEVELOPMENT.md)中多轮分析提炼出的约束性治理契约:五大核心原则(代码质量与架构纪律、测试驱动变更、CLI 用户体验一致性、离线优先性能纪律、最小依赖与幂等文件操作)外加安全/跨平台约束、开发流程质量门和治理机制。读完本文,你将理解这份宪法如何与仓库中真实存在的注册表架构、Typer 命令结构、目录栈(catalog stack)模型和离线打包机制一一对应,并掌握在贡献代码时如何满足这些 MUST 级门禁。
宪法定位:约束谁、约束什么
宪法的开篇明确了适用范围:Spec Kit 指 specify-cli 包及其捆绑资产,是一个本地、离线可用的开发者 CLI,用于为 AI 编码代理启动和运行规格驱动开发(SDD)工作流。这些原则对所有变更具有约束力——包括 specify bundle 子命令,以及任何未来的命令组、集成(integration)、扩展(extension)、预设(preset)或工作流(workflow)。
文件顶部的 HTML 注释是一份 Sync Impact Report(同步影响报告),记录了版本从 (template/unratified) 升级为 1.0.0 的判定:这是一次 MAJOR 基线,因为它在原先不存在强制治理的地方建立了有约束力的规则。报告同时确认了四个模板的对齐情况:
- templates/plan-template.md — 通用的 "Constitution Check" 门禁保持有效,计划阶段由原则 I–V 具体填充;
- templates/spec-template.md — 无宪法特定占位符,无需改动;
- templates/tasks-template.md — 任务分类(setup/foundational/story/polish)已涵盖测试、性能与 UX 任务,无需改动;
.github/agents/speckit.*.agent.md— 命令指引与 agent 无关,无需改动。
这种"宪法修订必须同步更新依赖模板并在 Sync Impact Report 中登记"的机制,正是后文 Governance 章节的核心要求。
原则 I:代码质量与架构纪律
宪法第一条要求代码库遵循严格的、注册表驱动的、分层架构,所有变更必须保持这一结构。结合仓库源码,该原则的每一条都有可验证的落点:
1. CLI 表层与可导入逻辑分离。 面向用户的命令放在 Typer 子应用中(如 src/specify_cli/commands/、各包下的 _commands.py);业务逻辑则放在普通的、可独立导入的模块中,不带 @app.command() 装饰器。新功能的编排逻辑 MUST 能够独立于 Typer 被测试。从源码结构看,src/specify_cli/integrations/ 正是这一模式的样板:命令层在 _commands.py,而安装、状态查询、迁移等逻辑分散在 _install_commands.py、manifest.py 等纯逻辑模块中。
2. 使用既定的扩展模式。 新的 agent/integration MUST 继承标准基类之一——MarkdownIntegration、TomlIntegration、YamlIntegration、SkillsIntegration——并声明所需的类属性(key、config、registrar_config,以及适用时的 context_file)。只有在没有任何基类合适时才可继承 IntegrationBase,且偏差必须说明理由。这一要求在 base.py 中得到直接印证:IntegrationBase 的 docstring 明确要求子类设置 key("unique identifier, matches actual CLI tool name")、config(与 AGENT_CONFIG 条目兼容的 dict)、registrar_config(与 CommandRegistrar.AGENT_CONFIGS 兼容的 dict),并可选设置 invoke_separator、multi_install_safe。
3. 尊重单一事实来源。 内建组件通过对应注册表接线,导入与注册保持字母序;重复的 key MUST 大声失败而非静默覆盖。integrations/init.py 中的 _register() 函数精确实现了这一契约——空 key 抛 ValueError,重复 key 抛 KeyError("Integration with key ... is already registered.")。而 _register_builtins() 中 38 个内建集成的导入与注册列表全部按字母序排列,kiro_cli/、cursor_agent/ 等包目录用下划线、key 保持连字符形式(如 key = "kiro-cli"),与宪法"包目录用下划线、key 保持规范(通常连字符)形式"的规定完全一致。对于 CLI 型集成,key 必须与可执行文件名匹配,使 shutil.which(key) 能够解析到二进制。
4. 命名与类型不是可选项。 私有模块/函数以 _ 前缀开头且 MUST NOT 跨包边界导入;每个新模块以 from __future__ import annotations 开头并使用现代类型语法(dict[str, Any]、str | None),拒绝旧式 Dict/List/Optional。仓库中 base.py 第一行导入就是 from __future__ import annotations,__init__.py 中 INTEGRATION_REGISTRY: dict[str, IntegrationBase]、get_integration(key: str) -> IntegrationBase | None 也都是现代语法实例。
宪法给出的 Rationale 解释了为什么:注册表 + 基类的架构是数十个集成、扩展、工作流能够以最小耦合共存的根本,这里的漂移会成倍放大维护成本,并打破"加一个子类、注册一次、附带一个测试"(add one subclass, register once, ship a test)的契约。
原则 II:测试驱动变更(不可协商)
第二条是宪法中标注 NON-NEGOTIABLE 的条款:每个行为变更 MUST 附带自动化测试,测试套件是硬性门禁。
1. 测试门禁合并。 CI 在操作系统 × Python 版本的矩阵上运行 pytest,变更必须通过矩阵中每一个单元格。查看当前仓库的 .github/workflows/test.yml,实际矩阵为 ubuntu-latest / windows-latest / macos-latest × Python 3.13 / 3.14(宪法批准时记录的是 3.11–3.13 组合,pyproject.toml 中 requires-python = ">=3.11" 为下限),但"全矩阵通过"这一门禁语义不变。此外 ruff 检查在独立的 job 中先行执行。
2. 一致性(parity)不变量必须成立。 每个集成 MUST 存在于注册表中、在需要时拥有 CommandRegistrar 配置条目、并附带专属测试文件 tests/integrations/test_integration_<key>.py(key 中的连字符在文件名中变为下划线)。这些由参数化测试(如 tests/integrations/test_registry.py)强制,MUST NOT 被弱化。仓库中 tests/integrations/ 目录下 test_integration_claude.py、test_integration_kiro_cli.py 等数十个文件正是这一不变量的实物证据——每个内建集成一个测试文件,与 _register_builtins() 的字母序注册一一对应。
3. 遵循 pytest 约定。 测试模块/类/函数使用项目配置好的 test_* / Test* 命名(见 pyproject.toml 的 [tool.pytest.ini_options]:python_files = ["test_*.py"]、python_classes = ["Test*"]、python_functions = ["test_*"],addopts 包含 --strict-markers);用 tmp_path、monkeypatch 和 autouse 的 auth 隔离 fixture 隔离状态;平台相关测试必须加守卫(如 @requires_bash),而不是放任其失败。test.yml 中的注释也证实了 Windows 上 bash 依赖测试的自动跳过策略(tests/conftest.py::_has_working_bash(),WSL launcher 因无法处理原生 Windows 路径而被拒绝)。
4. 安全与幂等性测试是强制类别。 路径穿越拒绝、清单哈希完整性/符号链接安全、不覆盖幂等性已由现有测试套件覆盖(如 tests/test_extension_add_path_traversal.py、tests/test_registrar_path_traversal.py、tests/test_check_prerequisites_paths_only.py);凡触及文件写入、路径处理或 setup 脚本的变更 MUST 扩展(绝不缩减)这些覆盖。
5. 网络全部 mock。 任何测试不得发起真实的外部网络调用;HTTP MUST 被 stub,使套件确定、可离线运行。
宪法的 Rationale 指出:支持 agent 的广度与离线/断网保证,只能靠详尽的参数化测试维持;parity 和安全套件正是阻止"单个新集成让整个矩阵回归"的防线。
原则 III:CLI 与用户体验一致性
第三条要求 CLI 呈现一个连贯的表层,每个命令组都必须"感觉像其他命令组":
- 复用共享动词词汇。 面向消费者的命令组使用既定动词——
list、add/install、remove、search、info、update,以及适用时的enable/disable、set-priority。现有动词能表达时 MUST NOT 新造动词;真正的新动词必须说明理由。 - 镜像目录栈(catalog-stack)模型。 由 catalog 支撑的命令组 MUST 暴露
<group> catalog list|add|remove,背后是优先级有序的源栈(数字越小优先级越高)加每源安装策略(install-allowedvsdiscovery-only),且无项目配置时回退到内建默认栈。仓库源码完整印证了这套模型:src/specify_cli/bundler/services/catalog_stack.py 定义了CatalogStack类;src/specify_cli/bundler/models/catalog.py 的模块 docstring 声明优先级为project > user > built-in,并定义策略常量INSTALL_ALLOWED = "install-allowed"与DISCOVERY_ONLY = "discovery-only";而 primitives.py 中的报错信息 "Preset '...' is from a discovery-only catalog" 则展示了策略在执行期的实际生效方式。 - 标准方式注册子应用。 命令组是
typer.Typer(...)实例,通过app.add_typer(child, name="...")挂载,最好经由模块化register(app)函数并在__init__.py中导入;嵌套 MUST 控制在约 2–3 层以内。 - 输出一致且机器友好。 人类可读输出使用共享的 Rich 约定(如
[green]✓[/green]表示成功、[red]Error:[/red]加非零退出码表示失败、消息中包含可操作的修复建议);提供--json标志时,合法 JSON 走 stdout,其余日志重定向到 stderr。 - 交互安全且幂等。 破坏性操作在确认前展示将发生什么变更;"already installed / already present" 的结果应当成功(退出码 0)而非报错;面向用户的命令组 MUST 在
docs/reference/下有文档。
Rationale 一语中的:可预测性就是产品。用户学一套动词、一套 catalog 模型、一套输出口语,然后应用到每个命令组——包括 specify bundle。
原则 IV:离线优先的性能与资源纪律
第四条确立了 Spec Kit 作为本地 CLI 的性能契约:响应速度、离线可操作、优雅降级。
specify init与核心脚手架 MUST 完全离线可用,基于捆绑的core_pack资产;资产解析 MUST 优先捆绑资产,其次源码检出,最后才考虑网络。pyproject.toml 的[tool.hatch.build.targets.wheel.force-include]段是这一原则的直接实现:模板、templates/commands、三种语言的scripts/、捆绑扩展(git、agent-context、assess、bug)、workflows/speckit、预设(lean、constitution-sync)乃至社区 bundle catalog 快照,全部被 force-include 进 wheel 的specify_cli/core_pack/命名空间,注释明确写着 "Bundle core assets sospecify initworks without network access (air-gapped / enterprise)"。tests/contract/test_wheel_core_pack_scripts.py 则从契约层面守护 wheel 中这些核心脚本的完整性。- 网络使用是惰性的、有界的、可降级的。 网络调用只发生在用户显式命令下;MUST 设置超时;MUST 缓存 catalog 结果(1 小时 TTL)并在失败时回退到过期缓存;MUST 将离线/限流状况表达为清晰消息而非崩溃。
- 保持启动开销低。 避免在 import 时加入重量级工作;新的可选子系统 SHOULD 优先惰性加载而非无条件 eager import,使无关命令(包括
--help)保持快速。 - 文件系统写入最小且幂等。 安装 MUST 跟踪文件(SHA-256 清单)、避免破坏用户修改过的内容、只卸载哈希仍匹配的文件、绝不跟随符号链接逃出项目根目录。
Rationale:开发者在断网(air-gapped)、企业、弱网环境中运行此工具;离线优先行为和幂等的、哈希跟踪的文件操作,是它可以被反复安全且快速运行的原因。
原则 V:最小依赖与安全、幂等的文件操作
第五条守护项目的依赖面与磁盘足迹:
- 默认零新增运行时依赖。 运行时依赖集被刻意保持精简并锁定在最低主版本。查看 pyproject.toml,当前依赖恰好是宪法列出的九个:
typer、click、rich、platformdirs、readchar、pyyaml、packaging、pathspec、json5。新增依赖需要维护者同意,且须论证现有依赖无法满足需求;新子系统 SHOULD 在进程内复用既有原语机制(见 src/specify_cli/bundler/services/primitives.py)而非重新实现或重新分发。 - 所有路径都经过校验。 任何来自用户/清单/catalog 输入的项目相对路径 MUST 被限制在项目根内(
Path.relative_to检查)并拒绝穿越载荷;符号链接逃逸 MUST 被拒绝。 - 错误显式且链式。 输入预先校验;抛出带可操作上下文的异常(违规字段/值加提示);用
raise ... from exc保留原因;可能合法失败的 I/O MUST 优雅降级而非抛出原始 traceback。 - 版本遵循 SemVer。 用户可见与打包行为变更遵循 MAJOR.MINOR.PATCH 语义;不向后兼容的变更 MUST 被明确指出并论证。
Rationale:精简且锁定的依赖集加上加固的幂等文件处理,是工具在企业与断网环境中值得信赖、且维护成本可控的根本。
安全与跨平台约束
宪法在五大原则之外单列了三项硬性约束:
- 跨平台一致性是强制的。 代码 MUST 在 Linux、macOS、Windows 上运行,Python 范围为 3.11–3.13(
pyproject.toml声明requires-python = ">=3.11")。Windows 细节(UTF-8 流重配置、bash 依赖测试自动跳过)MUST 被尊重;不得在没有守卫回退的情况下引入仅 POSIX 的假设。 - 安全工具是门禁。 CodeQL 与项目安全测试套件(路径穿越、清单/符号链接加固)MUST 保持绿色——对应 .github/workflows/codeql.yml 与
security.yml流水线。网络访问在测试中默认关闭、运行时可选开启、超时受限、凭证隔离。 - 格式化被强制。 .editorconfig 规则(LF 行尾、末尾换行、无行尾空格、Python 4 空格 / YAML-JSON-Markdown 2 空格)、
ruff check src/与markdownlint-cli2(配置见 .markdownlint-cli2.jsonc)MUST 通过。此外,pyproject.toml 的 ruff 配置锁定了 subprocess 安全姿态:S602/S604/S605规则意味着任何shell=True或os.system的重新引入都必须带显式# noqa注释,让偏差在评审中可见——这正是"安全测试与静态检查互为门禁"的具体体现。
开发工作流与质量门
宪法将日常开发流程也变成了契约:
- 分支命名遵循
<type>/<number>-<short-slug>(无 issue 时<type>/<short-slug>),<type>∈ {feat, fix, docs, community, chore}。 - PR 保持聚焦,MUST 满足:通过
ruff、pytest(全矩阵)、markdown lint、CodeQL;为新行为新增/扩展测试;行为变化时更新面向用户的文档(README.md、docs/、spec-driven.md);披露所使用的任何 AI 辅助。 - 影响 slash 命令的变更 MUST 通过编码代理手动演练,并在 PR 中报告结果(按 CONTRIBUTING.md)。
- 大型或横切变更(新模板、新参数、新命令组)MUST 在实现前与维护者达成一致。
治理机制:权威、修订与合规审查
宪法的 Governance 章节规定了它的自我维护方式:
- 权威。 原则 I–V 是约束性门禁;templates/plan-template.md 中的
## Constitution Check小节 MUST 对照这些原则评估,/speckit.analyze将违反 MUST 的冲突视为 CRITICAL。冲突的解决方式是修改 spec、plan 或 tasks——而不是稀释原则。plan 模板第 108 行附近的注释("Fill ONLY if Constitution Check has violations that must be justified")与这一机制呼应。 - 修订。 对宪法本体的修改需要一个含理由的 PR、维护者批准、并按以下策略升版;任何修订 MUST 在同一变更中传播到依赖的模板与命令指引,并记录在本文件顶部的 Sync Impact Report 中——文件头部那份 v1.0.0 报告本身就是范例。
- 版本策略(治理也用 SemVer)。 MAJOR = 不向后兼容的治理变更或原则的移除/重定义;MINOR = 新原则/新章节或实质性扩展的指引;PATCH = 澄清与非语义性润色。
- 合规审查。 每个 PR 与每次评审 MUST 核验对原则的合规性;新增复杂度或任何偏差 MUST 在 PR 中论证(对 plan 而言则在 plan 的 Complexity Tracking 节中);未论证的违规阻塞合并。
文档末尾标记:Version 1.0.0 | Ratified 2026-06-19 | Last Amended 2026-06-19。
结语
Spec Kit 的宪法展示了 SDD 方法论的一个容易被忽视的面向:不仅规格驱动功能开发,也用治理文档驱动工程纪律本身。它的独特之处在于"从现状提炼而非凭空立规"——注册表的重复 key 大声失败(integrations/init.py)、core_pack 的离线捆绑(pyproject.toml)、catalog 优先级栈(catalog_stack.py)在宪法写下之前就已存在,宪法只是把这些隐含约定升级为显式的 MUST 门禁,并配套了可执行的验证手段(CI 全矩阵、CodeQL、parity 参数化测试、Sync Impact Report)。对于贡献者而言,理解这份宪法等于拿到了 specify-cli 项目的架构地图:任何新集成、新命令组或新扩展,只要对照五大原则逐条自查,就能通过仓库设定的一切质量门。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00