内容
# specdrift
确定性规范检查,用于基于清单的黄金路径。
黄金路径的成败取决于一个承诺:**清单是唯一的真相来源。**
模式验证清单的形状,而静态分析器保护代码 — 但没有人检查清单与代码之间的空隙,那里是 "清单说X,仓库做Y" 默默腐烂的地方。这个空隙正是AI生成的更改腐烂的地方。specdrift是针对该空隙的检查层,它的设计故意保持简单:**它从不调用LLM — LLM调用它。**
```bash
dotnet tool install -g specdrift
specdrift validate .platform/manifest.yaml --schema manifest.schema.json --rules rules.yaml
```
## 它做什么(v1)
- **`validate`** — YAML/JSON清单的JSON模式验证 **以及交叉字段不变性** ,您在数据中声明为`rules.yaml`: "当此路径包含该值时,必须存在其他路径。" 每个发现都有规则ID、严重性和一条教导修复的消息。输出:人类文本或 `--format json` ;退出代码是合约(0 clean · 1 findings · 2 usage/IO)。
模式评估是一个**树内、无依赖评估器** ,适用于实际使用的2020-12子集清单(`type · enum · const · required · properties · additionalProperties · unevaluatedProperties · oneOf/anyOf/allOf/not · if/then/else · $ref(#/…) · pattern · min/maxLength · min/maximum · items · uniqueItems · min/maxItems`)。未知的断言关键字是**硬故障** — 引擎永远不会猜测。报告的设计是无噪音的:失败的`if`仅取消其分支,失败的`oneOf`仅在决策点产生一个发现,而不是每个替代方案一个。(为什么是树内:每个候选库都未能通过该项目自己的关卡 — 其中一个在NuGet二进制文件中提供维护费EULA,其中一个在运行时编译代码,其中一个默默地跳过`if/then`。)
- **`drift`** — 清单 ↔ 仓库现实,由漂移配置文件(`.specdrift/drift.yaml`)驱动:通过其软件包或线路调用启用的功能(每个方向都有自己的发现 — 包括 "引用但未启用" 的无效负载),提交的API文档在语义上与构建的文档不同,以及清单模式版本偏差。检测是**文本** 设计,并且说明了这一点;报告永远不会自动修复。
```yaml
# .specdrift/drift.yaml
version: 1
manifest: .platform/manifest.yaml
schemaVersion: 1
wiring:
- feature: features.outbox
package: Platform.Messaging
call: AddPlatformOutbox
- feature: providers.auth # 值受线路控制
equals: openid
package: Platform.Auth
call: AddPlatformAuth
openapi:
- committed: specs/openapi.json
built: artifacts/openapi.json
```
- **`mcp`** — 通过stdio MCP提供相同的两个动词(`spec_validate`、`spec_drift`),因此编码代理询问引擎而不是猜测。像任何stdio服务器一样注册它:
```json
{ "mcpServers": { "specdrift": { "command": "specdrift", "args": ["mcp"] } } }
```
## 配置文件是数据,引擎是通用的
specdrift 不了解任何特定平台。模式、变量规则和(随`drift`一起)线路表是**配置文件数据** ,每个黄金路径都在其自己的仓库中提供:
```yaml
# rules.yaml
version: 1
rules:
- id: SPEC0101
description: L2缓存需要Redis连接名称
when: { path: features.distributedCaching.levels, op: contains, value: l2 }
require: { path: features.distributedCaching.redis.connectionName }
severity: error
message: "features.distributedCaching.levels 包括'l2'但redis.connectionName缺失 - 命名缓存应使用的连接。"
```
支持的`when`操作:`exists`、`equals`、`contains`(数组成员或子字符串)。约束:`require`(路径必须存在且非空)和`forbid`(路径必须不存在)。没有`when`的规则无条件适用。
## 确定性合约
- 相同的输入 → 字节相同的报告。没有网络,没有时钟在输出中,没有LLM。
- 未知的清单模式版本是**硬故障** — 引擎永远不会猜测。
## 许可证
MIT。