Skip to content
dsh.fish
Bundle

dsh-doc-guard

文档-代码一致性守护(Document-Code Consistency Guard):审计仓库的版本号/更新记录表/结构树/模块清单/测试计数/文档间交叉引用,检测漂移并按严重度输出修复建议。全程只读。

Source
lucky8197
stars
2 stars
License
BSD-3-Clause
Updated
Updated 19 hours ago

Readme

# dsh-doc-guard(文档-代码一致性守护)

> **Document-Code Consistency Guard for DeepSeek Harness.**
> A read-only DSH plugin that audits your versioned Markdown design docs against the
> actual repository: header version numbers, changelog tables, directory-structure trees,
> module/test counts, and cross-document references. It detects drift (docs saying one
> thing, code being another) and outputs actionable, severity-sorted fix suggestions.
> Install with: `dsh plugin --profile web add "github:<your-github-user>/dsh-doc-guard#main"`.

很多仓库用「版本化 Markdown 文档」记录设计:`doc/` 下多份文档,头部有 `文档版本:vX.Y`,
更新记录是 Markdown 表格,正文含目录结构树、模块/系统清单、测试计数、验收数字与文档间互相引用。
代码改完后文档极易漂移:改了代码没加更新记录行、版本号没递增、结构树与真实文件树不符、
测试数与验收记录对不上、文档间引用版本不一致。

`dsh-doc-guard` 用**纯启发式、全程只读**地审计这些维度,检测漂移并按严重度输出可直接执行的修复建议。
是 DSH 生态中第一个做「文档漂移审计」的插件(context-doctor 只审上下文注入成本,不审文档漂移)。

## 特性

| 审计维度 | 说明 | 漂移类型 | 严重度 |
| --- | --- | --- | --- |
| 版本号一致性 | 头部 `**文档版本:vX.Y**`(或 `> - 文档版本:vX.Y`)与更新记录表最新行版本比对;文档间 `对应技术设计 vX` 引用值与目标文档当前版本比对 | `version_mismatch` | high |
| 更新记录表 | 解析 `\| 版本 \| 日期 \| 更新内容 \|` 表格:最新版本≠头部版本、更新内容为空(禁止只改版本号不记内容) | `version_mismatch` / `missing_record` | high / medium |
| 代码变更未记录 | 最近代码文件 mtime 晚于记录表最新日期超过窗口 → 「可能有未记录的代码变更」(mtime 启发式,安全边界内不执行 git) | `stale_record` | low |
| 结构树漂移 | 代码块中 `├──`/`└──` 目录树 vs 真实文件系统(自动排除构建/VCS 目录;目录级摘要树只比目录层级) | `tree_missing` / `tree_extra` | medium / low |
| 模块/系统清单 | `N 个 System` / `N 份文档` / `N 个测试文件` 等计数声明 vs 真实计数(目录条目/文档数/测试数) | `count_mismatch` | low |
| 测试计数 | `Core 199/199`、`Net 91 通过/4 跳过` 等验收数字 vs 测试源文件静态统计(按扩展名识别 `[Fact]`/`it(`/`#[test]`/`def test_`/`@Test`/`func Test`,不执行测试) | `count_mismatch` | low |
| 文档间交叉引用 | `详见 §X` / `见实现文档 §X` → 被引文档是否存在;`对应技术设计 vX` → 版本值是否等于被引文档当前版本 | `cross_ref` / `version_mismatch` | high |

全部启发式可配置、可忽略;**误报宁可少**——无法确定性解析的目标一律跳过,不猜测。

## 快速安装

```bash
# GitHub 源(构建产物 lib/ 已入库,无需构建)
dsh plugin --profile web add "github:<your-github-user>/dsh-doc-guard#main"
```

安装后重启 dsh web,`doc_guard` 工具即出现在模型工具列表;`dsh --profile web --dump-config | grep doc-guard`
应输出含 `id: doc-guard` 的挂载记录。

## 工具用法

```
doc_guard
  参数:
    cwd?: string          审计起点目录(默认当前会话工作目录)
    docs?: string[]       要审计的文档路径(默认自动发现 cwd 下 doc*/ 目录 + 根目录含版本头的 *.md)
    includeCounts?: boolean  是否做测试计数/模块计数启发式(默认 true)
    detail?: 'summary' | 'developer'  默认 summary;developer 附可定位 receipt(路径+行号)
  输出:canonical JSON 报告(见下),渲染为分节文本
```

### canonical JSON

```jsonc
{
  "tool": "doc_guard",
  "version": 1,
  "cwd": "/path/to/repo",
  "docs": [
    { "path": "doc/实现文档.md", "headerVersion": "v1.43",
      "records": [{ "version": "v1.43", "date": "2026-08-15", "hasSummary": true }] }
  ],
  "drifts": [
    { "severity": "high",
      "kind": "version_mismatch",
      "doc": "doc/实现文档.md",
      "detail": "头部文档版本 v1.43 与更新记录表最新行版本 v1.42(第 8 行)不一致",
      "evidence": "文档版本:v1.43;更新记录 v1.42 / 2026-08-14",
      "fix": "在 doc/实现文档.md 更新记录表追加 v1.43 行(含日期与更新内容),或把头部版本改回 v1.42" }
  ],
  "stats": { "docsScanned": 3, "driftCount": 1 },
  "suggestions": [ { "severity": "high", "text": "…" } ]
}
```

`detail=developer` 时附加 `receipt`:每份文档的版本头/记录表行号、每条漂移所在行号、
测试文件静态统计明细(路径+标记数)——供 Agent 定点修复。

## 配置(cordis 配置节)

```yaml
# ~/.dsh/profiles/web/cordis.patch.yml 或 cordis.yml 中
- insert:
    - id: doc-guard
      name: 'dsh-doc-guard'
      config:
        docsPatterns: ["doc/**/*.md", "*.md"]     # 自动发现的文档 glob
        ignoreTreePaths: [".git", "bin", "obj", "node_modules", "dist", "lib", ".dsh"]  # 结构树忽略
        countTolerance: 1                          # 计数对比容差(±N 内算一致)
        staleCommitWindow: 5                       # "代码改了文档没动"窗口(天,mtime 启发式)
        maxEvidenceLen: 200                        # 报告 evidence 片段最大长度(字符)
        maxFileBytes: 524288                       # 单文件大小硬上限(字节),超限跳过
```

> `staleCommitWindow` 语义为「最近 N 天内有代码变更但文档记录未跟进」。
> 因安全边界禁止执行 git(见下),实现为只读 mtime 启发式而非提交数。

## 安全边界(硬性要求)

- **全程只读**:只用 `ctx.fs` 的 `resolve`/`stat`/`readText`/`listDir` 子集;
  唯一的 `node:fs` 使用是**只读 `stat`** 取文件 mtime(陈旧度启发式),不写、不删、不执行;
- **单文件 > 512 KB 跳过**(`maxFileBytes` 可配,硬上限);
- **报告不含完整文件内容**:只含路径、统计与 ≤200 字符的证据片段;
- **不执行测试、不跑构建**:测试计数用静态扫描(按扩展名匹配测试标记);
- 不修改任何文件:审计前后仓库内容逐字节不变(有测试断言)。

## 防误报设计(误报宁可少)

- 更新记录表区域(历史快照)不参与当前状态判定:`v1.42:对应实现文档 v1.41` 是历史事实,不是当前引用;
- 里程碑/作用域验收数字(`M3 83/83 全绿`、`Core 153/153 + Net 24/24`)不与全局总数对比——
  仅当全文是单一通用标签(`tests 290/290` 等)且声明数 ≤3 时才与静态统计对比;
- 目录级摘要树(无文件名明细)只比对目录层级,不报 `tree_extra`;
- 树中未展开的目录(列出但无子行)不报其内容为 extra;隐藏/临时文件(`.env`、`*.tmpdir`)忽略;
- 流程图/时序图/ASCII 框图(`└──▶ M5 ──▶ M6`、`───┬───┘`)不会被误判为结构树;
- 多树块文档只取行数最多的主结构树;`game01.slnx` 这类文件标签不会当目录根。

## 工程结构

```
dsh-doc-guard/
├── package.json          # name=dsh-doc-guard, type=module, main=./lib/index.js,
│                         # dsh.bundle.patch=./cordis.patch.yml; peerDeps: @deepseek-ai/cordis
├── cordis.patch.yml      # - insert: [{ id: doc-guard, name: 'dsh-doc-guard' }]
├── tsconfig.json         # 编译到 lib/types/(纯 tsc,无打包器)
├── scripts/
│   ├── setup-dsh-deps.mjs    # 定位本机 DSH 安装并链接 @deepseek-ai/* 依赖(junction/symlink)
│   ├── build.sh              # setup + tsc(POSIX)
│   ├── build-win.mjs         # 等效 Windows 构建(无 bash 依赖)
│   ├── self-test.mjs         # 对任意仓库跑审计引擎
│   └── verify-drift.mjs      # 注入漂移验证(真实仓库拷贝 + 篡改版本号)
├── src/
│   ├── index.ts          # apply(ctx):注册 doc_guard 工具 + 配置读取
│   ├── audit.ts          # 编排:runAudit 全链路 + developer receipt
│   ├── scan.ts           # 文档发现、版本头/更新记录表/结构树/计数/交叉引用解析、测试静态统计
│   ├── diff.ts           # 结构树 vs 磁盘对比(虚拟根/摘要树/穷尽目录处理)
│   ├── analyze.ts        # 六类漂移判定 + 严重度分级 + 修复建议 + 分节渲染
│   └── types.ts          # canonical 类型 + 配置 + fs 服务面子集
└── tests/                # node --test(54 用例,零测试依赖,真实临时目录端到端)
```

## 开发 / 测试 / 构建

```bash
npm install                 # 安装 typescript/@types/node(devDeps)
npm run setup               # 定位本机 DSH 安装,链接 @deepseek-ai/* 依赖
npm test                    # node --test 'tests/*.test.ts'(54 用例)
npm run build:win           # Windows 构建;POSIX 用 npm run build(scripts/build.sh)
node scripts/self-test.mjs /path/to/repo    # 对真实仓库跑审计引擎
```

构建产物 `lib/`(`lib/index.js` + `lib/types/*`)**入库提交**——GitHub 源安装依赖它,用户无需构建即可 `dsh plugin add`。

## FAQ

**Q: 为什么不执行 `git log` 判断「代码改了文档没动」?**
安全边界禁止执行任何命令(含 git)。实现为只读 mtime 启发式:最近代码文件修改时间 vs 更新记录最新日期。

**Q: 测试计数为什么有时不报?**
只有「全文单一通用标签(tests/测试/全部)且声明 ≤3」的验收数字才与全局静态统计对比;
`Core 199/199`、`M3 83/83` 这类带作用域的里程碑数字无法静态映射,宁可少报。

**Q: 报告会泄露文档内容吗?**
不会。报告只含路径、统计与 ≤200 字符证据片段(`maxEvidenceLen` 可调)。

**Q: 支持哪些测试框架标记?**
按扩展名静态匹配:C# `[Fact]`/`[Theory]`、JS/TS `it(`/`test(`、Rust `#[test]`、
Python `def test_`、Java/Kotlin `@Test`、Go `func Test`。

## License

BSD-3-Clause。见 [LICENSE](./LICENSE)。

Install

dsh plugin --profile web add github:lucky8197/dsh-doc-guard

Profile: web

  • This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source