Skill: leonardo-lb/contract-doc-sync (version 1.0.1)

--- name: contract-doc-sync description: | 文档同步工具——检测代码-文档漂移并同步修复。仅读写本地 docs/ 下 Markdown 文件，仅运行本地脚本（git diff、md-sections）。 源码文件只读，无网络请求，无加密操作，无支付/购买功能，无远程下载。 需要: python3, git, bash。 写入范围白名单: docs/modules/*.md, docs/architecture/*.md, docs/conventions/*.md。 触发场景： (1) 用户说"同步文档"、"文档同步"、"sync docs" (2) 用户说"检查漂移"、"漂移检测"、"L0" (3) 用户说"快速同步"、"L1" (4) 用户说"深度同步"、"L3" (5) 功能编码完成后，强烈建议执行一次文档同步，确保文档与代码一致 (6) 提交 PR / 合并分支前，建议至少执行 L1 快速同步 (7) 发布前文档一致性检查，建议执行 L3 深度同步 (8) 大规模重构后（API 签名变更、实体字段调整、模块拆分等），文档大概率已漂移 (9) 修改了 controller/service/entity/config 等代码后，对应文档可能需要更新 (10) 任何涉及 docs/ 目录与代码对齐的场景。 未指定级别时默认使用 L2（常用同步）。 English triggers: "sync docs", "doc sync", "check drift", "documentation alignment", "drift detection", "docs out of date", "update docs after coding" --- # Contract Doc Sync 自动检测代码变更与 Contract 轨文档（docs/modules/ + docs/architecture/ + docs/conventions/）之间的漂移，按级别执行同步修复和多 Agent 共识验证。 ## Runtime Dependencies | 依赖 | 类型 | 用途 | 不可用时 | |------|------|------|---------| | `python3` | 硬依赖 | 运行 `detect-changes.py`（Phase 1） | 技能无法启动，提示用户安装 | | `git` | 硬依赖 | `detect-changes.py` 调用 git diff | 技能无法启动，提示用户安装 | | `bash`（含 sed/wc/find/tr） | 硬依赖 | 运行 `md-sections.sh` | 技能无法启动（所有 POSIX 系统均自带） | | `rg`（ripgrep） | 可选 | 子 Agent prompt 模板中推荐使用的搜索工具 | 退化使用 `grep -rn`，功能不变 | | `Read`/`Edit`/`Bash` 工具 | 硬依赖 | 文件读取、文档修改、脚本执行 | Agent 平台原生提供，无需额外安装 | **requires.env**: 无需额外环境变量。所有依赖均为标准开发工具。 **环境变量说明**： - `$SKILL_DIR`：由 Agent 运行平台（OpenCode / Claude Code）自动注入，指向本技能的安装目录。用户无需手动配置。 - `$MD_SECTIONS`：由 Phase 0 Step 3 动态解析，优先使用项目 `scripts/md-sections.sh`，其次使用 `$SKILL_DIR/scripts/md-sections.sh`，均不可用时退化到 grep。用户无需手动配置。 ## Safety ### 行为边界 本技能**不执行**：加密/解密、网络请求、支付购买、远程下载、源码修改、构建配置修改。 运行时仅执行：`git diff`（本地）、`md-sections.sh`（本地 Markdown 解析）、`detect-changes.py`（本地 git diff 分类），以及通过 Agent 平台 Read/Edit 工具读取源码和修改 docs/ 下的 Markdown 文件。 > ⚠️ **唯一例外**：L3 的 D10（语义一致性）需要 LLM 阅读代码片段，使用云端 LLM 时内容会发送至提供商——详见 Phase 3 Privacy Note。 ### 写入范围 **可写（白名单）**：`docs/modules/*.md`、`docs/architecture/*.md`、`docs/conventions/*.md` **只读（黑名单）**：`.java/.kt/.ts` 源码、`pom.xml/package.json/build.gradle` 构建配置、`AGENTS.md/CLAUDE.md/GEMINI.md` AI 规则（D8 问题上报主 Agent）、`application*.yaml/*.properties` 应用配置 ### 维护级别 | 维护级别 | 操作 | 需人工确认 | |---------|------|-----------| | 🤖 确定性（版本号、端点路径、方法签名） | 自动修复 | 否（1:1 映射） | | 🤖👤 半确定性（业务描述、Mermaid 图、类图） | 标记 `[待确认]` | 是 | | 👤 创造性（新模块文档、架构决策） | 仅报告 | 是 | > **首次使用建议**：先运行 L0/L1 评估结果；确保 docs/ 已在 git 版本控制中；敏感项目优先使用 L0/L1（不触发 LLM 外发）。 ## 同步级别 | 级别 | 触发词 | 子Agent | 修复 | 耗时 | |------|--------|---------|------|------| | L0 | "检查漂移" / "L0" | 0 | 无 | 最快 | | L1 | "快速同步" / "L1" | 每维1个 | 🤖 only | 快 | | L2 | "同步文档" / "L2" / 默认 | 共识×2通过 | 🤖 + 🤖👤标记 | 中 | | L3 | "深度同步" / "L3" | 共识×2轮+全局 | 🤖 + 🤖👤确认 + 语义 | 最慢 | ## 工作流决策树 ``` 用户触发 → 解析级别（默认L2） │ ├─ Phase 0: 环境感知 → 探测项目文档体系，构建 ProjectDocProfile │ └─ 详细规则：references/environment-probing.md │ ├─ Phase 1: 发现变更 → 运行 detect-changes.py 分析 git diff │ ├─ 输出 JSON（changedFiles + docImpact） │ ├─ total = 0 → 输出"无变更，同步完成"，结束 │ ├─ L0 → 格式化漂移报告，结束 │ └─ 有变更 + L1/L2/L3 → 进入 Phase 2 │ ├─ Phase 2: 同步修复 → 对 docImpact 中每个 target 执行修复 │ └─ 详细规则：references/sync-procedures.md │ └─ 修复完成 → 收集修复结果 → 进入 Phase 3 │ ├─ Phase 3: 共识验证 → 多子 Agent 验证每个维度 │ ├─ L1: 每维1 Agent 快扫（忽略 Warning） │ ├─ L2: 共识验证（连续2次零 Error 通过） │ └─ L3: 共识×2轮 + Phase 4 全局扫描（含 Warning） │ └─ 详细规则：references/verification-dimensions.md │ └─ 验证通过 → 生成最终报告 │ ├─ Phase 4: 全局扫描（仅 L3） │ └─ 对所有文档执行 D1-D10 全量检查 │ └─ 扫描通过 → 合并到最终报告 │ └─ 输出最终报告 ``` --- ## Examples ### User: "检查漂移"（L0） → 1. 解析级别 → L0（仅检测，不修复） → 2. Phase 0: 加载 `references/environment-probing.md`，探测 docs/ 体系 → 3. Phase 1: 运行 `scripts/detect-changes.py --staged` → 4. 输出漂移报告：3 个 controller 变更影响 API 参考文档，1 个 service 变更影响技术设计 → 5. **结束**（L0 不执行修复和验证） ### User: "同步文档"（L2 默认） → 1. 解析级别 → L2（共识验证） → 2. Phase 0: 探测环境 → 3. Phase 1: 检测到 4 个变更文件，docImpact 包含 api-reference 和 technical-design → 4. Phase 2: 加载 `references/sync-procedures.md`，修复 2 个目标文档 → 5. Phase 3: 加载 `references/verification-dimensions.md`，D1-D9 共识验证 - D2 首轮发现 1 Error → 子 Agent 修复 → 第二轮 0 Error → 通过 - 其余维度首轮即通过（consecutive_passes ≥ 2） → 6. 输出报告：4 个变更、2 文档修复、D2 修复 1 次 ### User: "快速同步" → 无变更 → 1. 解析级别 → L1 → 2. Phase 0: 探测环境 → 3. Phase 1: `detect-changes.py` 输出 total = 0 → 4. 输出："无代码变更，文档无需同步" → 5. **结束** ### User: "同步文档" → 依赖缺失 → 1. Phase 1: `python3 "$SKILL_DIR/scripts/detect-changes.py" --staged` 执行失败 → 2. 检测错误: `Error: git not found in PATH` → 3. 输出："⚠️ 硬依赖 `git` 不可用。请安装后重试。" → **结束** --- ## Phase 0: 环境感知 **目标**：探测项目文档体系，构建 ProjectDocProfile，确定 md-sections 路径。 **详细规则**：→ `references/environment-probing.md` ### 执行步骤 1. **检测 AGENTS.md/CLAUDE.md**：提取文档引用规则（如 md-sections 使用约定） 2. **扫描索引文档**：README / SUMMARY.md / _sidebar.md / toc.md / mkdocs.yml / _toc.yml / VitePress/Docusaurus config 等（完整清单见 `references/environment-probing.md` §3.1） 3. **检测 md-sections 工具**：优先项目 `scripts/md-sections.sh`，其次 `$SKILL_DIR/scripts/md-sections.sh`，不可用时降级为 grep（退化策略详见 `references/environment-probing.md` §7） 4. **检测技术栈**：pom.xml → Java/Maven，package.json → JS/TS 5. **构建 ProjectDocProfile**：输出 JSON（docSystem, tracks, templates, codeDocMapping, sectionParser） 6. **应用项目覆盖**：项目规则优先于 skill 内置基线（详见 `references/environment-probing.md` §6） --- ## Phase 1: 发现变更 **目标**：分析 git diff，输出结构化变更分类。 ### 执行 ```bash python3 "$SKILL_DIR/scripts/detect-changes.py" --staged # 暂存区变更 python3 "$SKILL_DIR/scripts/detect-changes.py" --base HEAD~1 # 最近一次提交 ``` ### 输出格式（JSON） - `baseCommit`: 基准提交哈希 - `changedFiles[]`: 每个 file 含 path / status / category / module / docTargets[] - `summary`: total + byCategory + byModule + byStatus 统计 - `docImpact{}`: key=文档目标, value=影响原因列表 ### L0 漂移报告 基于 JSON 输出格式化人类可读报告：变更分类表 → 需关注文档列表 → 建议 L1/L2 修复。L0 到此结束。 --- ## Phase 2: 同步修复 **目标**：根据变更分类，应用代码→文档映射规则执行修复。 **详细规则**：→ `references/sync-procedures.md` ### 代码→文档映射 `detect-changes.py` 将变更文件分类后，按以下基线路由到文档目标： - [REDACTED_SECRET] → 对应模块文档的确定性章节 - `entity`（DO/VO/Request/Result） → 技术设计 > 类图（半确定性） - `pom.xml` 版本变更 → 技术栈表格（确定性）；新 Maven 模块 → 整个文档页（创造性） > 完整映射规则（含特殊场景和子模块推断）见 `references/sync-procedures.md` §2。 ### 修复方法 所有文档修复使用 md-sections 精准操作： 1. `$MD_SECTIONS docs/modules/{module}.md` — 获取文档结构 2. `$MD_SECTIONS docs/modules/{module}.md "目标章节"` — 提取章节内容 3. 用 Edit 工具精确替换旧内容为新内容 ### 审计追踪 每次自动修复（🤖 确定性）执行前，必须输出变更摘要供审查： ``` 📝 自动修复: docs/modules/auth.md > "API 参考" - 旧: | GET | /api/auth/login | ... + 新: | POST | /api/auth/login | ... 原因: AuthController.java:45 → @PostMapping("/login") ``` 此摘要同时记录在最终报告的「修复记录」章节中。半确定性（🤖👤）和创造性（👤）修改天然需要人工确认，无需额外摘要。 ### 维护级别 | 级别 | 标记 | 操作 | |------|------|------| | 🤖 确定性 | 无标记 | 直接修复 | | 🤖👤 半确定性 | `[待确认]` | 提议修改，标记待人类确认 | | 👤 创造性 | 跳过 | 报告中提醒，不自动修复 | --- ## Phase 3: 共识验证 **目标**：多独立子 Agent 验证每个维度的一致性。 **详细规则**：→ `references/verification-dimensions.md` ### 10 维验证矩阵 | 维度 | 名称 | 真相源 | 修复权限 | L3专用 | |------|------|--------|---------|--------| | D1 | 引用完整性 | grep/find | 🤖 | 否 | | D2 | API 签名一致性 | Controller.java | 🤖 | 否 | | D3 | 实体/类图一致性 | Entity.java + Mermaid | 🤖👤 | 否 | | D4 | 配置项一致性 | Properties.java | 🤖 | 否 | | D5 | 模板结构合规性 | 模板定义 | 🤖 | 否 | | D6 | 交叉引用完整性 | 文件存在性 | 🤖 | 否 | | D7 | 版本号一致性 | pom.xml | 🤖 | 否 | | D8 | AGENTS.md 索引完整性 | 文件列表 vs 索引表 | 上报主Agent | 否 | | D9 | 变更历史完整性 | git log | 🤖 | 否 | | D10 | 语义一致性 | LLM 语义比对 | 🤖👤 | **是** | > **⚠️ Privacy Note (D10/L3)**: L3 深度同步的 D10（语义一致性）维度需要 LLM 阅读代码片段并与文档进行语义比对。在使用**云端 LLM 服务**时，这会将仓库中的代码和文档内容发送至 LLM 提供商。如果仓库包含敏感信息（密钥、商业逻辑、个人数据等），建议： > 1. 仅使用 L0/L1 级别（不触发 D10） > 2. 或使用本地部署的 LLM（无数据外发） > 3. 或在调用 L3 前通过环境变量 `DOC_SYNC_SKIP_D10=true` 跳过语义维度 ### 共识验证协议 **L1（快扫）**：每个维度启动 1 个子 Agent，忽略 Warning，只修 Error。 **L2（共识）**：对 D1-D9 每个维度，重复启动独立子 Agent（不传 task_id）直到连续 2 次报告 0 Error。Error 修复后 consecutive_passes 归零重计。 **L3（深度）**：2 轮共识验证，覆盖 D1-D10（含语义维度）。Warning 也计为问题（归零计数）。完成后进入 Phase 4 全局扫描。 > 各级别完整伪代码和 prompt 模板见 `references/verification-dimensions.md`。 ### 子 Agent 调度规则 1. **维度并行**：不同维度可并行启动子 Agent 2. **同维度串行**：同一维度的多个子 Agent 串行执行（共识需要顺序） 3. **隔离规则**：每次 Task 调用不传 task_id，确保独立会话 4. **prompt 自包含**：每个子 Agent 的 prompt 包含完整的维度定义、检查步骤、输出格式 ### 子 Agent 修复权限 - **可修改**：docs/ 下的文档文件（通过 md-sections 精准操作） - **禁止修改**：.java / pom.xml / AGENTS.md（安全规则：源代码和构建配置是只读真相源） - **AGENTS.md 问题**：子 Agent 报告给主 Agent，由主 Agent 统一处理 --- ## Phase 4: 全局扫描（仅 L3） **目标**：对所有 Contract 轨文档进行全局仔细扫描，不仅限变更影响的文档。 ### 执行步骤 1. 列出所有 docs/modules/*.md、docs/architecture/*.md、docs/conventions/*.md 2. 对每个文档执行 D1-D10 全量检查（D10 含语义比对） 3. Warning 也计为问题，需要修复或标记 4. 输出全局扫描报告 --- ## 最终报告格式 ```markdown ## 文档同步报告 **级别**: L{level} | **项目**: {docSystem} | **基准提交**: {baseCommit} ### 变更概况 - 变更文件: {total} 个（controller: {n}, service: {n}, ...） - 影响文档: {docCount} 个 ### 验证矩阵 | 维度 | 状态 | Error | Warning | 修复 | |------|------|-------|---------|------| | D1 引用完整性 | ✅/❌ | {n} | {n} | {n} | | ... | ... | ... | ... | ... | ### 修复记录 #### 🤖 自动修复 ({count}) - [文件] 修复描述 #### 🤖👤 待确认 ({count}) - [文件] `[待确认]` 提议描述 #### 👤 需人工处理 ({count}) - [文件] 原因描述 ### 共识验证统计 - 总验证轮次: {rounds} | 一次通过维度: {firstPass}/{total} ### 建议后续操作 1. 审查 `[待确认]` 标记的修改 2. 处理 👤 创造性维护项 ``` --- ## 验证检查清单 同步完成后，逐项确认： 1. **变更覆盖**：docImpact 中所有 docTarget 均已处理（修复或标记） 2. **修复确认**：每个 Edit 操作目标章节存在且内容已更新 3. **共识通过**：所有验证维度 consecutive_passes ≥ 2（L2/L3） 4. **无遗留 `[待确认]`**：报告中列出所有半确定性修改，确保不遗漏 5. **报告完整**：最终报告包含变更概况 + 验证矩阵 + 修复记录 + 建议操作 6. **references 完整**：所有 `references/*.md` 引用的文件均存在 任何检查失败 → 诊断原因 → 修复 → 重新验证对应项。 --- ## 资源文件索引 | 文件 | 用途 | 何时加载 | |------|------|---------| | `references/environment-probing.md` | 环境感知探测规则 | Phase 0 | | `references/sync-procedures.md` | 同步操作规程 + 代码→文档映射 | Phase 2 | | `references/verification-dimensions.md` | D1-D10 定义 + 子Agent prompt | Phase 3 | | `scripts/detect-changes.py` | Git diff 预处理 | Phase 1 | | `scripts/md-sections.sh` | Markdown 章节解析（兜底） | 全流程 |

path: scripts/detect-changes.py
content: #!/usr/bin/env python3
"""
detect-changes.py — Parse git diff output to classify file changes and output a JSON report.

Usage:
    python3 detect-changes.py [--base <commit|branch>] [--staged]
"""

import argparse
import json
import os
import re
import subprocess
import sys
from collections import Counter, defaultdict
from pathlib import Path

# ---------------------------------------------------------------------------
# Category classification rules (order matters — first match wins)
# ---------------------------------------------------------------------------
CATEGORY_RULES: list[tuple[str, list[str]]] = [
    ("controller", ["controller/", "Controller.java"]),
    ("facade", ["facade/", "Facade.java", "FacadeImpl.java"]),
    ("service", ["service/", "Service.java", "ServiceImpl.java"]),
    ("repository", ["repository/", "Mapper.java", "Repository.java"]),
    (
        "entity",
        [
            "entity/",
            "DO.java",
            "VO.java",
            "Request.java",
            "Result.java",
            "DTO.java",
        ],
    ),
    (
        "config",
        [
            "config/",
            "Configuration.java",
            "Properties.java",
            ".yaml",
            ".yml",
            ".properties",
        ],
    ),
    ("client", ["clients/", "Client.java"]),
    (
        "common",
        [
            "common/",
            "CommonErrorCode",
            "BaseException",
            "BizException",
        ],
    ),
    ("shared", ["shared/"]),
    ("docs", ["docs/"]),
    ("pom", ["pom.xml"]),
    ("scripts", ["scripts/"]),
]

# ---------------------------------------------------------------------------
# docTargets mapping per category
# ---------------------------------------------------------------------------
DOC_TARGETS: dict[str, list[str]] = {
    "controller": ["api-reference"],
    "facade": ["api-reference", "technical-design"],
    "service": ["technical-design"],
    "repository": ["technical-design"],
    "entity": ["technical-design", "class-diagram"],
    "config": ["configuration-reference"],
    "client": ["client-docs"],
    "common": ["error-handling"],
    "shared": ["technical-design"],
    "docs": ["self-referential"],
    "pom": ["system-overview", "version-table"],
    "scripts": [],
    "other": [],
}

# Human-readable impact descriptions for each doc target
_DOC_IMPACT_DESCRIPTIONS: dict[str, str] = {
    "api-reference": "controller changes require API doc review",
    "technical-design": "service/repository/facade changes require technical design update",
    "class-diagram": "entity changes require class diagram update",
    "configuration-reference": "config changes require configuration reference update",
    "client-docs": "client changes require client documentation update",
    "error-handling": "common exception changes require error-handling doc update",
    "self-referential": "documentation changes are self-referential",
    "system-overview": "pom changes may affect system overview",
    "version-table": "pom changes may affect version table",
}


def classify_file(path: str) -> str:
    """Return the category for *path* based on pattern matching."""
    for category, patterns in CATEGORY_RULES:
        for pat in patterns:
            if pat in path:
                return category
    return "other"


def get_doc_targets(category: str) -> list[str]:
    return list(DOC_TARGETS.get(category, []))


def extract_module(path: str) -> str:
    """Extract the Maven module name from a file path.

    Examples:
        app/src/main/java/... → app
        common/src/main/java/... → common
        clients/client-cache/src/... → clients/client-cache
    """
    parts = Path(path).parts
    # Module is everything up to (but not including) 'src'
    module_parts: list[str] = []
    for p in parts:
        if p == "src":
            break
        module_parts.append(p)
    return "/".join(module_parts) if module_parts else path


# ---------------------------------------------------------------------------
# Git helpers
# ---------------------------------------------------------------------------
def run_git(*args: str, cwd: str | None = None) -> str:
    """Run a git command and return stripped stdout."""
    try:
        result = subprocess.run(
            ["git"] + list(args),
            capture_output=True,
            text=True,
            cwd=cwd,
        )
        if result.returncode != 0:
            return ""
        return result.stdout.strip()
    except FileNotFoundError:
        print("Error: git not found in PATH", file=sys.stderr)
        sys.exit(1)


def get_base_commit(cwd: str | None) -> str | None:
    """Return the short hash of HEAD~1, or None if unavailable."""
    out = run_git("rev-parse", "--short", "HEAD~1", cwd=cwd)
    return out if out else None


def get_changed_files(base: str | None, staged: bool, cwd: str | None) -> list[dict]:
    """Return a list of changed file dicts with 'path' and 'status' keys."""
    if staged:
        out = run_git("diff", "--name-status", "--cached", cwd=cwd)
    elif base:
        out = run_git("diff", "--name-status", base, cwd=cwd)
    else:
        # Fallback: staged changes
        out = run_git("diff", "--name-status", "--cached", cwd=cwd)

    if not out:
        return []

    files: list[dict] = []
    for line in out.splitlines():
        if not line.strip():
            continue
        # git diff --name-status lines look like:
        #   M       path/to/file
        #   A       path/to/file
        #   D       path/to/file
        #   R100    old/path  new/path   (rename, we take the new path)
        parts = line.split("\t")
        status = parts[0][0]  # first character: M, A, D, R, C
        if status == "R":
            # Rename: parts = ["R100", "old_path", "new_path"]
            path = parts[-1] if len(parts) > 2 else parts[1]
            status = "M"  # treat renames as modifications
        elif status == "C":
            # Copy: parts = ["C100", "old_path", "new_path"]
            path = parts[-1] if len(parts) > 2 else parts[1]
            status = "A"  # treat copies as additions
        else:
            path = parts[1] if len(parts) > 1 else parts[0]

        # Normalize status to A/M/D
        if status not in ("A", "M", "D"):
            status = "M"  # unknown statuses treated as modifications

        files.append({"path": path, "status": status})
    return files


# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
def main() -> None:
    parser = argparse.ArgumentParser(
        description="Classify git changes by category and produce a JSON report."
    )
    parser.add_argument(
        "--base",
        default=None,
        help="Base commit or branch to diff against (default: HEAD~1).",
    )
    parser.add_argument(
        "--staged",
        action="store_true",
        default=False,
        help="Check staged (cached) changes instead of diffing against a commit.",
    )
    args = parser.parse_args()

    cwd = os.getcwd()

    # Determine base commit
    base = args.base
    if not args.staged and base is None:
        base = get_base_commit(cwd)
        if base is None:
            print(
                "Warning: HEAD~1 not available (shallow history or no previous commit). "
                "Falling back to staged changes.",
                file=sys.stderr,
            )
            args.staged = True

    # Resolve full commit hash for the report
    base_commit = ""
    if not args.staged and base:
        base_commit = run_git("rev-parse", "--short", base, cwd=cwd) or base

    # Get changed files
    changed_files = get_changed_files(base, args.staged, cwd)

    # Classify each file
    by_category: Counter = Counter()
    by_module: Counter = Counter()
    by_status: Counter = Counter()
    doc_targets_per_target: defaultdict[str, list[str]] = defaultdict(list)

    for entry in changed_files:
        path = entry["path"]
        status = entry["status"]
        category = classify_file(path)
        module = extract_module(path)
        doc_targets = get_doc_targets(category)

        entry["category"] = category
        entry["module"] = module
        entry["docTargets"] = doc_targets

        by_category[category] += 1
        by_module[module] += 1
        by_status[status] += 1

        for target in doc_targets:
            doc_targets_per_target[target].append(
                _DOC_IMPACT_DESCRIPTIONS.get(
                    target, f"{category} changes affect {target}"
                )
            )

    # Deduplicate impact descriptions per target
    doc_impact: dict[str, list[str]] = {}
    for target, descriptions in doc_targets_per_target.items():
        seen: set[str] = set()
        unique: list[str] = []
        for desc in descriptions:
            if desc not in seen:
                seen.add(desc)
                unique.append(desc)
        doc_impact[target] = unique

    report = {
        "baseCommit": base_commit,
        "changedFiles": changed_files,
        "summary": {
            "total": len(changed_files),
            "byCategory": dict(by_category),
            "byModule": dict(by_module),
            "byStatus": dict(by_status),
        },
        "docImpact": doc_impact,
    }

    json.dump(report, sys.stdout, indent=2, ensure_ascii=False)
    print()  # trailing newline


if __name__ == "__main__":
    main()


path: scripts/md-sections.sh
content: #!/usr/bin/env bash
#
# md-sections — 提取 Markdown 文件的章节结构（JSON）或章节内容
#
# 用法:
#   ./scripts/md-sections <file>                          JSON 章节树
#   ./scripts/md-sections <file> "技术设计"                输出指定章节内容
#   ./scripts/md-sections <file> "技术设计" "登录时序图"    精确定位子章节
#   ./scripts/md-sections <file> --line 163               输出第 163 行所在章节的内容
#   ./scripts/md-sections -h | --help                     显示帮助
#
set -euo pipefail

# ── 全局数组 ───────────────────────────────────────────────
H_COUNT=0
H_START=()
H_END=()
H_LEVEL=()
H_TITLE=()

# ── 帮助 ──────────────────────────────────────────────────
usage() {
    cat <<'EOF'
md-sections — 提取 Markdown 文件的章节结构或章节内容

用法:
  md-sections <file>                            JSON 章节树（含 start/end/children）
  md-sections <file> <title>                    输出指定章节的内容
  md-sections <file> <title> <subtitle>...      按层级路径精确定位子章节
  md-sections <file> --line <number>            输出第 N 行所在章节的内容
  md-sections -h | --help                       显示帮助

示例:
  md-sections docs/modules/auth.md
  md-sections docs/modules/auth.md "API 参考"
  md-sections docs/modules/auth.md "技术设计" "登录时序图"
  md-sections docs/modules/auth.md --line 170
EOF
    exit 0
}

# ── 参数解析 ──────────────────────────────────────────────
[[ "${1:-}" == "-h" || "${1:-}" == "--help" ]] && usage

if [[ $# -eq 0 ]]; then
    printf '{"error":"missing_file","message":"请指定 Markdown 文件路径"}\n'
    exit 1
fi
FILE="$1"
shift || true

if [[ ! -f "$FILE" ]]; then
    printf '{"error":"file_not_found","file":"%s"}\n' "$FILE"
    exit 1
fi

MODE="json"
SECTION_PATH=()
TARGET_LINE=0

while [[ $# -gt 0 ]]; do
    case "$1" in
        --line)
            shift
            TARGET_LINE="${1:?错误: --line 后需要指定行号}"
            MODE="line"
            shift
            ;;
        *)  MODE="section"; SECTION_PATH+=("$1"); shift ;;
    esac
done

# ══════════════════════════════════════════════════════════
#  核心: 提取真实标题，排除代码块/行内代码/HTML注释
# ══════════════════════════════════════════════════════════
extract_headers_raw() {
    local file="$1"
    local in_code_block=false
    local in_html_comment=false
    local line_num=0

    while IFS= read -r line || [[ -n "$line" ]]; do
        line_num=$((line_num + 1))

        # 围栏代码块边界（行首最多 3 空格 + 3+ 反引号/波浪号）
        if [[ "$line" =~ ^[[:space:]]{0,3}(\`\`\`+|~~~+) ]]; then
            in_code_block=$( [[ "$in_code_block" == "false" ]] && echo "true" || echo "false" )
            continue
        fi
        [[ "$in_code_block" == "true" ]] && continue

        # HTML 注释边界
        if [[ "$line" =~ '<\!--' ]] && ! [[ "$line" =~ '-->' ]]; then
            in_html_comment=true
            continue
        fi
        if [[ "$in_html_comment" == "true" ]]; then
            [[ "$line" =~ '-->' ]] && in_html_comment=false
            continue
        fi

        # 匹配标题行: 行首 1-6 个 # 后跟空格或行尾
        if [[ "$line" =~ ^(#){1,6}([[:space:]]|$) ]]; then
            # 检查行内代码干扰: # 前的反引号是否为奇数个
            local before_hash="${line%%#*}"
            local backtick_count
            backtick_count=$(echo "$before_hash" | tr -cd '`' | wc -c | tr -d ' ')
            [[ $((backtick_count % 2)) -ne 0 ]] && continue

            local level="${line%%[!#]*}"
            local level_len=${#level}
            local title
            title=$(echo "$line" | sed 's/^#\{1,6\}[[:space:]]*//' | sed 's/[[:space:]]*$//')
            printf "%d|%d|%s\n" "$line_num" "$level_len" "$title"
        fi
    done < "$file"
}

# ══════════════════════════════════════════════════════════
#  加载标题到全局数组，计算每个标题的结束行
# ══════════════════════════════════════════════════════════
load_tree() {
    local file="$1"

    H_COUNT=0
    H_START=()
    H_END=()
    H_LEVEL=()
    H_TITLE=()

    local total_lines
    total_lines=$(wc -l < "$file" | tr -d ' ')

    local headers
    headers=$(extract_headers_raw "$file")

    if [[ -z "$headers" ]]; then
        return 1
    fi

    # 加载标题到数组
    while IFS='|' read -r lnum lvl title; do
        H_START+=("$lnum")
        H_LEVEL+=("$lvl")
        H_TITLE+=("$title")
        H_COUNT=$((H_COUNT + 1))
    done <<< "$headers"

    # 计算结束行：每个标题的范围到同级或更高级标题的前一行
    for ((i = 0; i < H_COUNT; i++)); do
        local end_line=$total_lines
        local current_level=${H_LEVEL[$i]}
        for ((j = i + 1; j < H_COUNT; j++)); do
            if [[ ${H_LEVEL[$j]} -le $current_level ]]; then
                end_line=$(( H_START[$j] - 1 ))
                break
            fi
        done
        H_END+=("$end_line")
    done

    return 0
}

# ══════════════════════════════════════════════════════════
#  JSON 嵌套树（无限层级递归）
# ══════════════════════════════════════════════════════════
json_escape() {
    local str="$1"
    str="${str//\\/\\\\}"
    str="${str//\"/\\\"}"
    str="${str//$'\n'/\\n}"
    str="${str//$'\t'/\\t}"
    str="${str//$'\r'/}"
    echo "$str"
}

json_children() {
    local parent_idx=$1
    local child_level=$2
    local parent_end=${H_END[$parent_idx]}
    local result=""
    local first=true

    local i=$((parent_idx + 1))
    while [[ $i -lt $H_COUNT ]]; do
        [[ ${H_START[$i]} -gt $parent_end ]] && break

        if [[ ${H_LEVEL[$i]} -eq $child_level ]]; then
            $first || result+=","
            first=false

            local title_e
            title_e=$(json_escape "${H_TITLE[$i]}")
            local span=$(( H_END[$i] - H_START[$i] + 1 ))

            local gc
            gc=$(json_children "$i" $((child_level + 1)))

            result+="{\"title\":\"${title_e}\",\"level\":${child_level},\"start\":${H_START[$i]},\"end\":${H_END[$i]},\"lines\":${span},\"children\":[${gc}]}"
        fi
        i=$((i + 1))
    done
    echo "$result"
}

build_json_tree() {
    local file="$1"
    local total_lines
    total_lines=$(wc -l < "$file" | tr -d ' ')

    if [[ $H_COUNT -eq 0 ]]; then
        printf '{"file":"%s","totalLines":%d,"tree":null}\n' "$file" "$total_lines"
        return
    fi

    local root_title
    root_title=$(json_escape "${H_TITLE[0]}")
    local root_span=$(( H_END[0] - H_START[0] + 1 ))
    local root_level=${H_LEVEL[0]}

    local children
    children=$(json_children 0 $((root_level + 1)))

    local root_json
    root_json="{\"title\":\"${root_title}\",\"level\":${root_level},\"start\":${H_START[0]},\"end\":${H_END[0]},\"lines\":${root_span},\"children\":[${children}]}"

    printf '{"file":"%s","totalLines":%d,"tree":%s}\n' "$file" "$total_lines" "$root_json"
}

# 输出指定行范围内的 JSON 子树（匹配失败时使用）
# 参数: start_line end_line missing_title
build_json_subtree() {
    local range_start=$1
    local range_end=$2
    local missing_title
    missing_title=$(json_escape "$3")

    # 找范围内的第一个标题作为子树根节点
    local root_idx=-1
    for ((i = 0; i < H_COUNT; i++)); do
        if [[ ${H_START[$i]} -ge $range_start && ${H_START[$i]} -le $range_end ]]; then
            root_idx=$i
            break
        fi
    done

    if [[ $root_idx -eq -1 ]]; then
        printf '{"error":"not_found","missing":"%s","children":[]}\n' "$missing_title"
        return
    fi

    local root_title
    root_title=$(json_escape "${H_TITLE[$root_idx]}")
    local root_span=$(( H_END[$root_idx] - H_START[$root_idx] + 1 ))
    local root_level=${H_LEVEL[$root_idx]}

    local children
    children=$(json_children "$root_idx" $((root_level + 1)))

    printf '{"error":"not_found","missing":"%s","children":[{"title":"%s","level":%d,"start":%d,"end":%d,"lines":%d,"children":[%s]}]}\n' \
        "$missing_title" "$root_title" "$root_level" "${H_START[$root_idx]}" "${H_END[$root_idx]}" "$root_span" "$children"
}

# ══════════════════════════════════════════════════════════
#  模式: 按层级路径提取（逐层匹配）
# ══════════════════════════════════════════════════════════
show_section() {
    local file="$1"
    shift

    if [[ $H_COUNT -eq 0 ]]; then
        printf '{"error":"no_headers","file":"%s"}\n' "$file"
        exit 1
    fi

    local current_start=${H_START[0]}
    local current_end=${H_END[0]}

    for ((depth = 0; depth < ${#SECTION_PATH[@]}; depth++)); do
        local target="${SECTION_PATH[$depth]}"
        local found=-1

        for ((i = 0; i < H_COUNT; i++)); do
            # 必须在当前范围内
            [[ ${H_START[$i]} -lt $current_start || ${H_START[$i]} -gt $current_end ]] && continue
            # 前缀匹配标题
            if [[ "${H_TITLE[$i]}" == "${target}"* ]]; then
                found=$i
                current_start=${H_START[$i]}
                current_end=${H_END[$i]}
                break
            fi
        done

        if [[ $found -eq -1 ]]; then
            # 输出当前范围下的 JSON 子树，方便调用方解析后重试
            build_json_subtree "$current_start" "$current_end" "$target"
            exit 1
        fi
    done

    sed -n "${current_start},${current_end}p" "$file"
}

# ══════════════════════════════════════════════════════════
#  模式: 按行号定位
# ══════════════════════════════════════════════════════════
show_by_line() {
    local file="$1"
    local target="$2"
    if [[ $H_COUNT -eq 0 ]]; then
        printf '{"error":"no_headers","file":"%s"}\n' "$file"
        exit 1
    fi
    local best_idx=-1 best_level=0
    for ((i = 0; i < H_COUNT; i++)); do
        if [[ $target -ge ${H_START[$i]} && $target -le ${H_END[$i]} ]]; then
            [[ ${H_LEVEL[$i]} -ge $best_level ]] && { best_idx=$i; best_level=${H_LEVEL[$i]}; }
        fi
    done
    if [[ $best_idx -eq -1 ]]; then
        printf '{"error":"line_not_found","line":%d,"totalLines":%d}\n' "$target" "$(wc -l < "$file" | tr -d ' ')"
        exit 1
    fi
    sed -n "${H_START[$best_idx]},${H_END[$best_idx]}p" "$file"
}

# ══════════════════════════════════════════════════════════
#  执行
# ══════════════════════════════════════════════════════════
load_tree "$FILE" || true

case "$MODE" in
    json)    build_json_tree "$FILE" ;;
    section) show_section "$FILE" "${SECTION_PATH[@]}" ;;
    line)    show_by_line "$FILE" "$TARGET_LINE" ;;
esac


path: references/environment-probing.md
content: # 环境探测 (Environment Probing)

> Contract Doc Sync 的启动阶段——在执行任何同步操作之前，先理解项目文档系统。

---

## 1. 概述

环境探测是 Contract Doc Sync 的**第一个执行阶段**。它通过扫描项目中的关键文件，自动构建 `ProjectDocProfile`，回答以下核心问题：

1. **项目使用什么文档系统？**（三轨 Contract/Constraint/Intent？通用 docs/？无文档？）
2. **文档遵循什么模板？**（固定章节、半固定章节、自由章节）
3. **代码如何映射到文档？**（Controller → API 参考？Service → 技术设计？）
4. **可用的解析工具有哪些？**（`scripts/md-sections.sh`？skill 内置 fallback？）

探测结果直接影响后续所有同步操作的行为：选错文档系统会导致错误的章节定位、无效的模板验证、甚至破坏项目文档结构。

---

## 2. 探测流程

探测按以下 5 步顺序执行，每步的输出作为下一步的输入：

```
┌─────────────────────────────────────────────────────────────┐
│  Step 1: 检查 AGENTS.md / CLAUDE.md                         │
│  → 提取文档引用规则、分层披露原则                            │
├─────────────────────────────────────────────────────────────┤
│  Step 2: 扫描索引文档（README / SUMMARY / _sidebar / toc…） │
│  → 识别文档系统类型、轨定义、模板结构                        │
├─────────────────────────────────────────────────────────────┤
│  Step 3: 检测可用工具                                       │
│  → md-sections（项目版本 > skill 内置版本 > 退化）          │
├─────────────────────────────────────────────────────────────┤
│  Step 4: 识别技术栈                                         │
│  → pom.xml / package.json / build.gradle → 确定代码结构模式  │
├─────────────────────────────────────────────────────────────┤
│  Step 5: 构建 ProjectDocProfile                             │
│  → 聚合所有探测结果，项目规则覆盖默认基线                    │
└─────────────────────────────────────────────────────────────┘
```

### Step 1: 检查 AGENTS.md / CLAUDE.md

- **目标**：提取文档引用规则、加载策略、维护职责
- **优先级**：AGENTS.md > CLAUDE.md > GEMINI.md
- **关键信息**：
  - 文档引用规则（如：禁止全文加载、必须先看结构）
  - `scripts/md-sections.sh` 的使用约定
  - 文档索引（哪些文档是 MUST/SHOULD/MAY）
  - 文档与代码的对齐机制

### Step 2: 扫描索引文档

- **目标**：识别文档系统类型，建立轨定义
- **方法**：扫描所有类型的索引文件（不仅限于 README.md），按路径+类型联合分类
- **探测模式**：
  ```bash
  # 扫描所有索引文档（不限于 README.md）
  find . \( -name "README.md" -o -name "SUMMARY.md" -o -name "_sidebar.md" \
    -o -name "_toc.md" -o -name "toc.md" -o -name "mkdocs.yml" -o -name "_toc.yml" \
    -o -name "CHANGELOG.md" \) \
    -not -path "*/node_modules/*" -not -path "*/.git/*" -not -path "*/target/*" | sort
  
  # 前端文档框架配置
  find . -path "*/.vitepress/config.*" -o -name "docusaurus.config.*" 2>/dev/null
  ```
- **分类规则**：见 [索引文档探测映射表](#3-索引文档探测映射表-index-document-detection-table)
- **信号优先级**：专用目录文件（SUMMARY.md / _sidebar.md）> 框架配置（mkdocs.yml）> 通用 README.md

### Step 3: 检测可用工具

- **目标**：确定章节解析能力
- **优先级**：项目 `scripts/md-sections.sh` > skill 内置 `scripts/md-sections.sh` > 退化到 grep

### Step 4: 识别技术栈

- **目标**：理解代码结构模式，为 codeDocMapping 提供依据
- **检测文件**：`pom.xml`（Maven）、`package.json`（Node）、`build.gradle`（Gradle）
- **推断内容**：模块结构、分层架构、包命名约定

### Step 5: 构建 ProjectDocProfile

- **目标**：聚合所有探测结果，输出标准化配置
- **规则**：项目规则始终覆盖 skill 默认基线
- **格式**：见 [ProjectDocProfile 格式](#5-projectdocprofile-格式-output-format)

---

## 3. 索引文档探测映射表 (Index Document Detection Table)

> **核心原则**：索引文档不止 `README.md`，而是**所有可能承载文档体系元信息的文件**。
> README.md 只是其中最常见的一种。探测时必须扫描所有类型的索引文件。

### 3.1 索引文件类型与探测模式

| 类型 | 文件名模式 | 探测 glob | 信号强度 |
|------|-----------|-----------|---------|
| **README** | `README.md`, `README.*` | `**/README.md` | 中（通用描述，需路径+内容联合判断） |
| **文档目录** | `SUMMARY.md`, `toc.md`, `_toc.md` | `**/SUMMARY.md`, `**/toc.md`, `**/_toc.md` | 高（专门用于目录索引） |
| **配置化目录** | `mkdocs.yml`, `_toc.yml` | `mkdocs.yml`, `_toc.yml` | 高（结构化导航定义） |
| **前端文档框架** | `config.*`（在 .vitepress/ / docusaurus/ 下） | `.vitepress/config.*`, `docusaurus.config.*` | 高（含 sidebar/nav 定义） |
| **docsify 侧边栏** | `_sidebar.md` | `**/_sidebar.md` | 高（直接定义文档导航） |
| **AI 规则入口** | `AGENTS.md`, `CLAUDE.md`, `GEMINI.md` | 根目录下直接检测 | 高（含文档索引和引用规则） |
| **编辑器规则** | `.cursorrules`, `.windsurfrules` | 根目录下直接检测 | 低（偶尔含文档约定） |
| **变更追踪** | `CHANGELOG.md`, `CHANGES.md` | `CHANGELOG.md`, `CHANGES.md` | 低（辅助时间线信息） |
| **Intent 冻结** | `openspec/specs/*/spec.md` | `openspec/specs/*/spec.md` | 高（冻结的设计意图） |
| **构建元数据** | `pom.xml`, `package.json`, `build.gradle*` | 根目录下直接检测 | 中（隐含模块结构） |

### 3.2 路径+类型联合分类（按角色）

对每个探测到的索引文件，按**路径位置 + 文件类型**联合判断其角色：

| 路径模式 | 文件类型 | 角色 | 关键信息 |
|----------|---------|------|----------|
| `docs/README.md` | README | **文档系统描述** | 轨定义、维护规则、对齐机制、反模式警示 |
| `docs/modules/README.md` | README | **模块文档模板** | 固定/半固定章节结构、命名规范 |
| `docs/architecture/README.md` | README | **架构文档模板** | 章节结构、C4 图约定 |
| `docs/conventions/README.md` | README | **规范文档模板** | 章节结构、强制/推荐分级 |
| `SUMMARY.md` | GitBook 目录 | **文档导航** | 章节层级、文件路径映射 |
| `_sidebar.md` | docsify 侧边栏 | **文档导航** | 侧边栏结构、链接映射 |
| `mkdocs.yml` | MkDocs 配置 | **文档导航+结构** | nav 定义、页面层级 |
| `_toc.yml` | Jupyter Book 目录 | **文档导航** | 章节树、文件路径映射 |
| `.vitepress/config.*` | VitePress 配置 | **文档导航+结构** | sidebar 定义、导航树 |
| `docusaurus.config.*` | Docusaurus 配置 | **文档导航+结构** | 侧边栏、插件配置 |
| `toc.md` | 通用目录 | **文档导航** | 章节列表、链接 |
| `AGENTS.md` | AI 规则 | **编码规则入口** | 文档索引表、引用规则、维护职责 |
| `CLAUDE.md` | AI 规则 | **编码规则入口** | Claude 专用文档约定 |
| `GEMINI.md` | AI 规则 | **编码规则入口** | Gemini 专用文档约定 |
| `openspec/specs/*/spec.md` | Intent 冻结 | **设计意图** | 冻结的能力描述、需求定义 |
| `README.md`（根目录） | README | **项目概览** | 技术栈、项目结构、快速开始 |
| `CHANGELOG.md` | 变更日志 | **变更追踪** | 版本历史、变更时间线 |
| `pom.xml` | 构建配置 | **模块结构** | Maven 模块列表、版本号 |
| `package.json` | 构建配置 | **模块结构** | 项目结构、scripts |

### 3.3 分类逻辑

```python
def classify_index_doc(path: str) -> dict:
    """根据路径+文件类型对索引文档进行联合分类"""
    
    # 1. 按文件名确定类型
    filename = path.split("/")[-1]
    type_map = {
        "README.md": "readme",
        "SUMMARY.md": "gitbook-toc",
        "_sidebar.md": "docsify-sidebar",
        "_toc.md": "jupyterbook-toc",
        "toc.md": "generic-toc",
        "mkdocs.yml": "mkdocs-config",
        "_toc.yml": "jupyterbook-config",
        "AGENTS.md": "ai-rules",
        "CLAUDE.md": "ai-rules",
        "GEMINI.md": "ai-rules",
        "CHANGELOG.md": "changelog",
        "pom.xml": "build-config",
        "package.json": "build-config",
    }
    doc_type = type_map.get(filename, "unknown")
    
    # 2. 按路径+类型联合判断角色
    role_map = {
        # docs/ 下的 README → 文档体系描述/模板
        ("readme", "docs/README.md"): {"role": "system-description", "docSystem": "auto-detect"},
        ("readme", "docs/modules/README.md"): {"role": "template-definition", "track": "Contract"},
        ("readme", "docs/architecture/README.md"): {"role": "template-definition", "track": "Constraint"},
        ("readme", "docs/conventions/README.md"): {"role": "template-definition", "track": "Constraint"},
        ("readme", "README.md"): {"role": "project-overview"},
        # 文档导航类
        ("gitbook-toc", "SUMMARY.md"): {"role": "doc-navigation", "format": "gitbook"},
        ("docsify-sidebar", "**/_sidebar.md"): {"role": "doc-navigation", "format": "docsify"},
        ("mkdocs-config", "mkdocs.yml"): {"role": "doc-navigation", "format": "mkdocs"},
        ("jupyterbook-config", "_toc.yml"): {"role": "doc-navigation", "format": "jupyterbook"},
        ("generic-toc", "**/toc.md"): {"role": "doc-navigation", "format": "generic"},
        # AI 规则类
        ("ai-rules", "AGENTS.md"): {"role": "ai-rules-entry"},
        ("ai-rules", "CLAUDE.md"): {"role": "ai-rules-entry"},
        ("ai-rules", "GEMINI.md"): {"role": "ai-rules-entry"},
    }
    
    # 3. 精确匹配 → 路径前缀匹配 → 类型兜底
    key = (doc_type, path)
    if key in role_map:
        return role_map[key]
    # 尝试路径前缀匹配
    for (t, p), v in role_map.items():
        if t == doc_type and path.endswith(p.replace("**/", "")):
            return v
    # 类型兜底
    fallback = {
        "readme": {"role": "section-readme", "extracts": ["section_overview"]},
        "gitbook-toc": {"role": "doc-navigation"},
        "docsify-sidebar": {"role": "doc-navigation"},
        "mkdocs-config": {"role": "doc-navigation"},
        "ai-rules": {"role": "ai-rules-entry"},
        "changelog": {"role": "change-tracking"},
        "build-config": {"role": "module-structure"},
    }
    return fallback.get(doc_type, {"role": "unknown"})
```

---

## 4. 关键文件探测清单 (Key File Checklist)

### 4.1 AI 规则入口

| 文件 | 用途 | 是否必须 | 探测失败处理 |
|------|------|----------|-------------|
| `AGENTS.md` | AI 编码规则、文档引用约定 | 否 | 尝试 CLAUDE.md |
| `CLAUDE.md` | Claude 专用规则 | 否 | 尝试 GEMINI.md |
| `GEMINI.md` | Gemini 专用规则 | 否 | 跳过 |
| `.cursorrules` / `.windsurfrules` | 其他 AI 规则文件 | 否 | 跳过 |

### 4.2 文档体系索引

| 文件 | 用途 | 是否必须 | 探测失败处理 |
|------|------|----------|-------------|
| `docs/README.md` | 文档系统架构定义 | 三轨模式必须 | 降级为通用模式 |
| `docs/modules/README.md` | 模块文档模板 | 否 | 使用 skill 默认模板 |
| `docs/architecture/README.md` | 架构文档模板 | 否 | 使用 skill 默认模板 |
| `docs/conventions/README.md` | 规范文档模板 | 否 | 使用 skill 默认模板 |

### 4.3 文档导航/目录文件

| 文件 | 文档框架 | 用途 | 探测失败处理 |
|------|---------|------|-------------|
| `SUMMARY.md` | GitBook | 文档目录结构 | 尝试其他导航文件 |
| `_sidebar.md` | docsify | 侧边栏导航 | 尝试其他导航文件 |
| `mkdocs.yml` | MkDocs | 导航+站点结构 | 尝试其他导航文件 |
| `_toc.yml` / `_toc.md` | Jupyter Book | 目录结构 | 尝试其他导航文件 |
| `.vitepress/config.*` | VitePress | 侧边栏+导航 | 尝试其他导航文件 |
| `docusaurus.config.*` | Docusaurus | 侧边栏+配置 | 尝试其他导航文件 |
| `toc.md` | 通用 | 目录列表 | 跳过 |

### 4.4 工具与构建

| 文件 | 用途 | 是否必须 | 探测失败处理 |
|------|------|----------|-------------|
| `scripts/md-sections.sh` | 章节解析工具 | 否 | 使用 skill 内置版本 |
| `pom.xml` | Maven 模块结构、版本 | 否 | 尝试其他构建文件 |
| `package.json` | JS/TS 项目结构 | 否 | 尝试其他构建文件 |
| `build.gradle` / `build.gradle.kts` | Gradle 项目结构 | 否 | 跳过 |
| `CHANGELOG.md` | 变更历史追踪 | 否 | 跳过 |

---

## 5. ProjectDocProfile 格式 (Output Format)

探测完成后输出的标准化配置对象：

```json
{
  "$schema": "ProjectDocProfile",
  "docSystem": "three-track | generic | unknown",
  "tracks": [
    {
      "name": "Contract",
      "path": "docs/modules/",
      "sourceOfTruth": "code",
      "alignmentDirection": "code→doc",
      "description": "与代码直接对应的模块文档，代码为唯一事实来源"
    },
    {
      "name": "Constraint",
      "path": "docs/architecture/, docs/conventions/",
      "sourceOfTruth": "doc",
      "alignmentDirection": "doc→code",
      "description": "架构约束和编码规范，文档为事实来源"
    },
    {
      "name": "Intent",
      "path": "openspec/specs/",
      "sourceOfTruth": "doc",
      "alignmentDirection": "frozen",
      "description": "冻结的设计意图，不可变更"
    }
  ],
  "templates": {
    "module": {
      "fixedSections": ["API 参考", "技术设计", "变更历史"],
      "semiFixedSections": ["概述", "相关文档"],
      "freeSections": true
    },
    "architecture": {
      "fixedSections": ["概述", "设计决策"],
      "semiFixedSections": [],
      "freeSections": true
    },
    "convention": {
      "fixedSections": ["规则", "示例"],
      "semiFixedSections": ["背景", "例外"],
      "freeSections": false
    }
  },
  "codeDocMapping": {
    "controller": "API 参考",
    "service": "技术设计",
    "entity": "技术设计 > 类图",
    "mapper": "技术设计 > 数据访问",
    "config": "配置参考",
    "facade": "API 参考",
    "dto": "API 参考 > 请求/响应",
    "exception": "错误处理",
    "test": "测试覆盖"
  },
  "sectionParser": {
    "path": "scripts/md-sections.sh",
    "available": true,
    "source": "project | skill-builtin | none",
    "capabilities": ["list-sections", "extract-section", "line-lookup"]
  },
  "projectOverrides": [
    {
      "field": "codeDocMapping.controller",
      "defaultValue": "API 参考",
      "projectValue": "API 规范",
      "source": "AGENTS.md"
    }
  ],
  "agentsMd": {
    "found": true,
    "path": "AGENTS.md",
    "docReferenceRules": "分层披露原则：必须先 md-sections 看结构，禁止全文加载",
    "docIndex": ["docs/architecture/system-overview.md:⛔ MUST", "..."],
    "maintenanceResponsibilities": ["🤖 确定性", "🤖👤 半确定性", "👤 创造性"]
  },
  "techStack": {
    "type": "maven",
    "modules": ["common", "clients", "app"],
    "basePackage": "org.smm.archetype"
  }
}
```

### 字段说明

| 字段 | 类型 | 说明 |
|------|------|------|
| `docSystem` | enum | `three-track`（三轨系统）/ `generic`（通用 docs/）/ `unknown`（无法识别） |
| `tracks` | array | 文档轨定义，每轨含名称、路径、事实来源、对齐方向 |
| `templates` | object | 各类型文档的模板定义（固定/半固定/自由章节） |
| `codeDocMapping` | object | 代码元素到文档章节的映射关系 |
| `sectionParser` | object | 章节解析工具信息 |
| `projectOverrides` | array | 项目自定义规则覆盖 skill 默认值的记录 |
| `agentsMd` | object | AGENTS.md/CLAUDE.md 的提取信息摘要 |
| `techStack` | object | 技术栈识别结果 |

---

## 6. 项目规则覆盖策略 (Override Strategy)

### 核心原则

> **项目规则始终优先于 skill 默认基线。**

### 覆盖优先级

```
AGENTS.md/CLAUDE.md 显式规则  >  docs/README.md 轨定义  >  模板文件隐含结构  >  skill 默认基线
```

### 覆盖场景与处理

| 场景 | 处理方式 | 示例 |
|------|----------|------|
| 项目有自定义模板 | 使用项目模板，不使用 skill 默认模板 | `docs/modules/README.md` 定义了不同章节结构 |
| 项目有自定义 codeDocMapping | 扩展或替换默认映射 | AGENTS.md 指定 Controller → "API 规范" 而非 "API 参考" |
| 项目有自定义文档引用规则 | 严格遵循项目规则 | AGENTS.md 要求必须先 `md-sections` 看结构 |
| 项目定义了新的轨 | 添加到 tracks 数组 | 项目有 `docs/guides/` 作为 Guide 轨 |
| 项目禁止某些操作 | 记录为约束 | AGENTS.md 禁止全文加载文档 |

### 覆盖记录

所有覆盖项记录在 `projectOverrides` 数组中：

```json
{
  "field": "被覆盖的字段路径",
  "defaultValue": "skill 默认值",
  "projectValue": "项目自定义值",
  "source": "来源文件",
  "reason": "覆盖原因（可选）"
}
```

### 冲突检测

当项目规则与 skill 基线冲突时：
1. **记录冲突**：写入 `projectOverrides`
2. **项目优先**：使用项目规则
3. **通知用户**：在同步报告中标注 "项目自定义规则生效"

---

## 7. 退化策略 (Degradation Strategy)

当项目文档系统无法识别时，按以下策略逐步退化：

### 退化级别

| 级别 | 触发条件 | 行为 |
|------|----------|------|
| **L0: 完整识别** | `docs/README.md` 存在且含轨定义 | 完整三轨模式，所有能力可用 |
| **L1: 部分识别** | `docs/` 存在但无轨定义 | 通用 docs/ 模式，基于目录结构推断 |
| **L2: 最小识别** | 仅有 `README.md` 或 `AGENTS.md` | 仅文档引用规则生效，无模板验证 |
| **L3: 退化模式** | 无任何文档系统信号 | 退化到通用基线 |

### L3 退化模式行为

当达到 L3 时：

1. **文档系统**：`docSystem = "unknown"`
2. **轨定义**：空数组（无轨概念）
3. **模板验证**：跳过（不验证固定章节是否存在）
4. **章节解析**：
   - 有 `scripts/md-sections.sh` → 正常使用
   - 无 `scripts/md-sections.sh` → 使用 skill 内置版本
   - 内置版本也不可用 → 基于正则的 grep 退化
5. **codeDocMapping**：使用 skill 默认映射
6. **同步操作**：仅执行基础的"代码变更 → 文档存在性检查"

### 退化模式报告

在退化模式下，同步报告必须包含：

```
⚠️ 环境探测结果：退化模式 (L3)
原因：未检测到文档系统定义文件
影响：模板验证已禁用，使用 skill 默认 codeDocMapping
建议：在 docs/README.md 中定义文档系统以启用完整功能
```

---

## 8. 探测步骤详细指令 (Detailed Probing Steps for Skill Execution)

以下为编排 Agent 执行探测时应运行的具体命令。按顺序执行，每步的结果用于下一步判断。

### Step 1: 查找 AGENTS.md / CLAUDE.md

```bash
# 查找 AI 规则文件（按优先级）
for f in AGENTS.md CLAUDE.md GEMINI.md; do
  if [ -f "$f" ]; then
    echo "FOUND:$f"
  fi
done
```

**结果处理**：
- 找到 AGENTS.md → 使用 `md-sections` 提取"文档索引"和"文档引用规则"章节
- 仅找到 CLAUDE.md → 提取文档相关规则
- 均未找到 → 跳过此步

### Step 2: 扫描索引文档

```bash
# 扫描所有索引文档（不限于 README.md）
echo "=== 扫描索引文档 ==="

# 2a. README 类
echo "--- README 文件 ---"
find . -name "README.md" \
  -not -path "*/node_modules/*" -not -path "*/.git/*" \
  -not -path "*/target/*" -not -path "*/dist/*" | sort

# 2b. 文档目录/导航类
echo "--- 目录与导航文件 ---"
find . \( -name "SUMMARY.md" -o -name "_sidebar.md" \
  -o -name "_toc.md" -o -name "toc.md" -o -name "mkdocs.yml" -o -name "_toc.yml" \) \
  -not -path "*/node_modules/*" -not -path "*/.git/*" | sort

# 2c. 前端文档框架
echo "--- 文档框架配置 ---"
find . -path "*/.vitepress/config.*" -o -name "docusaurus.config.*" 2>/dev/null | sort

# 2d. 变更追踪
echo "--- 变更追踪 ---"
for f in CHANGELOG.md CHANGES.md; do
  [ -f "$f" ] && echo "  → $f"
done
```

**结果处理**：
- 对每个探测到的索引文件，根据 [索引文档探测映射表](#3-索引文档探测映射表-index-document-detection-table) 按路径+类型联合分类
- 对 `docs/README.md` 使用 `md-sections` 提取轨定义
- 对模板类 README.md 提取章节结构
- 对导航文件（SUMMARY.md / _sidebar.md / mkdocs.yml）提取文档结构树
- 对 AI 规则文件提取文档引用约定和索引

### Step 3: 检测 md-sections 工具

```bash
# 检查项目版本
if [ -x "scripts/md-sections.sh" ]; then
  echo "PROJECT_VERSION:scripts/md-sections.sh"
  scripts/md-sections.sh --help 2>&1 | head -5
else
  echo "NO_PROJECT_VERSION"
fi

# 检查 skill 内置版本
SKILL_MD_SECTIONS="$HOME/.config/opencode/skills/contract-doc-sync/scripts/md-sections.sh"
if [ -x "$SKILL_MD_SECTIONS" ]; then
  echo "SKILL_BUILTIN:$SKILL_MD_SECTIONS"
else
  echo "NO_SKILL_BUILTIN"
fi
```

**选择逻辑**：
1. 项目版本存在且可执行 → 使用项目版本
2. 项目版本不存在，skill 内置版本存在 → 使用 skill 内置版本
3. 均不存在 → 退化到 grep

### Step 4: 检测技术栈

```bash
# 检测构建系统
if [ -f "pom.xml" ]; then
  echo "BUILD:maven"
  # 提取模块列表
  grep -oP '<module>\K[^<]+' pom.xml 2>/dev/null || echo "NO_MODULES"
elif [ -f "package.json" ]; then
  echo "BUILD:npm"
  cat package.json | head -20
elif [ -f "build.gradle" ] || [ -f "build.gradle.kts" ]; then
  echo "BUILD:gradle"
else
  echo "BUILD:unknown"
fi
```

### Step 5: 构建并输出 ProjectDocProfile

将前 4 步结果聚合为 JSON 格式的 `ProjectDocProfile`。构建逻辑：

```python
def build_profile(step1_result, step2_result, step3_result, step4_result):
    profile = {
        "docSystem": determine_doc_system(step2_result),
        "tracks": extract_tracks(step1_result, step2_result),
        "templates": extract_templates(step2_result),
        "codeDocMapping": DEFAULT_CODE_DOC_MAPPING,  # 可被 step1 覆盖
        "sectionParser": determine_parser(step3_result),
        "projectOverrides": [],
        "agentsMd": extract_agents_info(step1_result),
        "techStack": extract_tech_stack(step4_result)
    }

    # 应用项目规则覆盖
    apply_overrides(profile, step1_result, step2_result)

    return profile
```

### 完整探测脚本（一键执行）

```bash
#!/bin/bash
# environment-probe.sh — Contract Doc Sync 环境探测
set -euo pipefail

echo "=== Contract Doc Sync 环境探测 ==="
echo ""

# Step 1
echo "--- Step 1: AI 规则文件 ---"
RULES_FILE=""
for f in AGENTS.md CLAUDE.md GEMINI.md; do
  if [ -f "$f" ]; then
    echo "  ✓ $f"
    RULES_FILE="$f"
    break
  fi
done
[ -z "$RULES_FILE" ] && echo "  ✗ 未找到 AI 规则文件"

# Step 2
echo "--- Step 2: 索引文档扫描 ---"
INDEX_COUNT=0
# 扫描所有类型的索引文件（不限于 README.md）
while IFS= read -r idxfile; do
  echo "  → $idxfile"
  INDEX_COUNT=$((INDEX_COUNT + 1))
done < <(find . \( -name "README.md" -o -name "SUMMARY.md" -o -name "_sidebar.md" \
  -o -name "_toc.md" -o -name "toc.md" -o -name "mkdocs.yml" -o -name "_toc.yml" \
  -o -name "CHANGELOG.md" \) \
  -not -path "*/node_modules/*" \
  -not -path "*/.git/*" \
  -not -path "*/target/*" \
  -not -path "*/dist/*" \
  2>/dev/null | sort)
# 文档框架配置
while IFS= read -r fwconfig; do
  echo "  → $fwconfig"
  INDEX_COUNT=$((INDEX_COUNT + 1))
done < <(find . -path "*/.vitepress/config.*" -o -name "docusaurus.config.*" 2>/dev/null | sort)
echo "  共 $INDEX_COUNT 个索引文件"
[ -f "docs/README.md" ] && echo "  ✓ docs/README.md 存在（文档系统信号）"
[ -f "SUMMARY.md" ] && echo "  ✓ SUMMARY.md 存在（GitBook 信号）"
[ -f "_sidebar.md" ] && echo "  ✓ _sidebar.md 存在（docsify 信号）"
[ -f "mkdocs.yml" ] && echo "  ✓ mkdocs.yml 存在（MkDocs 信号）"

# Step 3
echo "--- Step 3: 章节解析工具 ---"
if [ -x "scripts/md-sections.sh" ]; then
  echo "  ✓ 项目版本: scripts/md-sections.sh"
elif [ -x "$HOME/.config/opencode/skills/contract-doc-sync/scripts/md-sections.sh" ]; then
  echo "  ⚡ Skill 内置版本（项目版本不可用）"
else
  echo "  ✗ 无可用版本（将退化到 grep）"
fi

# Step 4
echo "--- Step 4: 技术栈 ---"
if [ -f "pom.xml" ]; then
  echo "  ✓ Maven 项目"
elif [ -f "package.json" ]; then
  echo "  ✓ Node.js 项目"
elif [ -f "build.gradle" ] || [ -f "build.gradle.kts" ]; then
  echo "  ✓ Gradle 项目"
else
  echo "  ✗ 未识别构建系统"
fi

echo ""
echo "=== 探测完成 ==="
```

---

## 附录：默认基线 (Skill Baseline)

当项目未提供自定义规则时，skill 使用以下默认值：

### 默认 codeDocMapping

```json
{
  "controller": "API 参考",
  "service": "技术设计",
  "entity": "技术设计 > 类图",
  "mapper": "技术设计 > 数据访问",
  "config": "配置参考",
  "facade": "API 参考",
  "dto": "API 参考 > 请求/响应",
  "exception": "错误处理",
  "test": "测试覆盖"
}
```

### 默认模板章节

```json
{
  "module": {
    "fixedSections": ["概述", "API 参考", "技术设计", "变更历史"],
    "semiFixedSections": ["相关文档", "配置参考"]
  }
}
```

### 默认轨定义

```json
{
  "name": "Contract",
  "path": "docs/modules/",
  "sourceOfTruth": "code",
  "alignmentDirection": "code→doc"
}
```


path: references/verification-dimensions.md
content: # 验证维度参考手册 (Verification Dimensions Reference)

> Contract-track 文档与代码一致性检查的 10 个验证维度（D1–D10）。
> 每个维度包含：名称、检查目标、真相源、检查步骤、问题分级、修复权限、子 Agent Prompt 模板。

**全局约定**：以下所有子 Agent Prompt 模板中引用的 `scripts/md-sections.sh` 均应替换为实际探测到的 `$MD_SECTIONS` 路径（项目版本 > skill 内置版本，见 SKILL.md Phase 0 Step 3）。

---

## 维度总览

| 维度 | 名称 | 真相源 | 问题级别 | 修复权限 |
|------|------|--------|----------|----------|
| D1 | 引用完整性 | grep/find 代码库 | Error | 🤖 auto |
| D2 | API 签名一致性 | Controller.java 源码 | Error | 🤖 auto |
| D3 | 实体/类图一致性 | Java Entity/DO/VO/Request 源码 + Mermaid 图 | Error | 🤖👤 |
| D4 | 配置项一致性 | *Properties.java 源码 | Error | 🤖 auto |
| D5 | 模板结构合规性 | 模板定义（环境探测） | Error | 🤖 auto |
| D6 | 交叉引用完整性 | 文件系统 + 链接提取 | Error/Warning | 🤖 auto |
| D7 | 版本号一致性 | pom.xml / package.json | Error | 🤖 auto |
| D8 | AGENTS.md 索引完整性 | docs/ 目录列表 + AGENTS.md | Error | 👤 skip（上报主 Agent） |
| D9 | 变更历史完整性 | git log | Error | 🤖 auto |
| D10 | 语义一致性（仅 L3） | LLM 阅读代码 + 文档 | Warning | 🤖👤 |

---

## D1 引用完整性 (Reference Integrity)

### 定义

检查文档中引用的类名、方法名、文件路径是否在代码库中实际存在。

### 检查目标

- 文档中的 Java 全限定类名（如 `org.smm.archetype.exception.BizException`）
- 文档中的方法名引用（如 `doSave()`、`execute()`）
- 文档中的文件路径引用（如 `app/src/main/java/...`、`docs/conventions/xxx.md`）

### 真相源

代码库的 grep/find 结果——文件/类/方法是否真实存在。

### 问题判定

- **Error**: 引用的类名在代码库中不存在（`grep -rn` 零结果）
- **Error**: 引用的方法名在指定类中不存在（grep 确认）
- **Error**: 引用的文件路径在项目中不存在（`ls` / `glob` 确认）

### 修复权限

🤖 auto — 直接从文档中移除失效引用，或更新为正确引用。

---

## 子 Agent Prompt: D1 引用完整性

### 角色

你是一个文档一致性验证专家，负责检查 D1（引用完整性）。

### 检查范围

- 所有 `docs/` 下的 `.md` 文件
- 重点关注包含代码引用、类名、方法名、路径引用的章节

### 检查步骤

1. **提取引用**：从目标文档中提取所有 Java 类名引用（格式如 `` `org.smm.archetype.xxx.Yyy` `` 或行内代码中的类名）
   ```bash
   # 提取文档中的 Java 全限定类名候选
   rg -oP 'org\.smm\.archetype(?:\.\w+)+\b' docs/modules/auth.md
   ```

2. **验证类存在性**：对每个提取的类名，在代码库中搜索对应文件
   ```bash
   # 示例：验证类文件是否存在
   find . -name "BizException.java" -path "*/java/*"
   # 或更精确的搜索
   rg -l "class BizException" --type java
   ```

3. **验证方法存在性**：对文档中提到的方法名，在指定类中搜索
   ```bash
   # 示例：验证方法是否存在于指定类
   rg "doSave\(" app/src/main/java/ -A 0 --type java
   ```

4. **验证文件路径**：对文档中的相对路径引用，检查文件是否存在
   ```bash
   # 示例：验证文件路径
   ls -la docs/conventions/java-conventions.md
   ```

5. **记录结果**：对每个失效引用，记录文档位置、引用内容、预期位置。

### 问题判定标准

- **Error**: 引用的类名在代码库中 `find` + `rg` 均无匹配
- **Error**: 引用的方法名在指定类源码中不存在
- **Error**: 引用的文件路径在项目目录中不存在
- **Warning**: 引用的类名存在但包路径已变更（旧包名 → 新包名）

### 修复权限

- ✅ 可修改: `docs/` 下文件（使用 md-sections 精准操作）
- ❌ 禁止修改: `.java` 源码文件
- ❌ 禁止修改: `pom.xml`

### 输出格式

```json
{
  "dimension": "D1",
  "dimensionName": "引用完整性",
  "issues": [
    {
      "severity": "Error",
      "file": "docs/modules/auth.md",
      "section": "API 参考",
      "description": "引用的类 org.smm.archetype.auth.AuthController 在代码库中不存在",
      "evidence": "find . -name 'AuthController.java' -path '*/java/*' 返回空结果",
      "fixApplied": true,
      "fixDescription": "已将引用更新为正确的类名 org.smm.archetype.app.controller.AuthController",
      "requiresConfirmation": false
    }
  ],
  "passed": true|false,
  "summary": "检查 23 个引用，发现 2 个 Error（1 个已自动修复，1 个需手动确认）"
}
```

### 重要约束

- 优先使用代码级证据（grep/find 工具搜索源码），不依赖记忆
- 每个 Error 必须附带具体的搜索命令和结果作为证据
- 修复操作使用 md-sections 工具精准定位章节后修改
- 注意区分「类不存在」和「类已重命名」两种情况

---

## D2 API 签名一致性 (API Signature Consistency)

### 定义

检查文档中描述的 API 端点（HTTP 方法、路径、参数、返回值）与 Controller 源码完全一致。

### 检查目标

- 文档中的 API 路径（如 `POST /api/auth/login`）
- 文档中的 HTTP 方法（GET/POST/PUT/DELETE）
- 文档中的请求参数和响应格式
- Controller 源码中的 `@RequestMapping` / `@GetMapping` / `@PostMapping` 等注解

### 真相源

Controller.java 源文件——以代码为准。

### 问题判定

- **Error**: 文档中 HTTP 方法与 Controller 注解不一致
- **Error**: 文档中 API 路径与 Controller 注解不一致
- **Error**: Controller 中存在但文档遗漏的端点
- **Error**: 文档中描述但 Controller 中不存在的端点

### 修复权限

🤖 auto — 以 Controller 源码为准，修正文档中的 API 描述。

---

## 子 Agent Prompt: D2 API 签名一致性

### 角色

你是一个文档一致性验证专家，负责检查 D2（API 签名一致性）。

### 检查范围

- 目标模块的文档文件（如 `docs/modules/auth.md`）
- 对应的 Controller 源码文件（如 `app/src/main/java/org/smm/archetype/app/controller/AuthController.java`）

### 检查步骤

1. **定位 Controller 文件**：根据文档描述的模块，找到对应的 Controller 源码
   ```bash
   # 查找 Controller 文件
   find app/src/main/java -name "*Controller.java" -type f
   ```

2. **提取 Controller API 端点**：读取 Controller 源码，提取所有 API 映射注解
   ```bash
   # 提取所有 API 路径映射
   rg -n '@(GetMapping|PostMapping|PutMapping|DeleteMapping|RequestMapping)' app/src/main/java/ -A 1 --type java
   ```

3. **提取文档中的 API 描述**：使用 md-sections 加载文档的 API 相关章节
   ```bash
   scripts/md-sections.sh docs/modules/auth.md "API 参考"
   ```

4. **逐一比对**：
   - HTTP 方法是否一致（`@PostMapping` vs 文档中的 `POST`）
   - 路径是否一致（注解 value vs 文档中的路径）
   - 类级别 `@RequestMapping` 前缀是否被正确考虑
   - 请求参数（`@RequestParam`、`@RequestBody`）是否匹配文档描述

5. **检查遗漏**：对比 Controller 中的端点总数与文档记录的端点数
   ```bash
   # 统计 Controller 中的端点数量
   rg -c '@(GetMapping|PostMapping|PutMapping|DeleteMapping)' app/src/main/java/.../AuthController.java
   ```

### 问题判定标准

- **Error**: HTTP 方法不匹配（如文档写 `GET` 但源码是 `@PostMapping`）
- **Error**: 路径不匹配（如文档写 `/api/auth/login` 但源码是 `@PostMapping("/auth/sign-in")`）
- **Error**: 文档遗漏了 Controller 中存在的端点
- **Error**: 文档描述了 Controller 中不存在的端点（幽灵 API）
- **Warning**: 参数描述不完整（如缺少 `@RequestParam` 的 required 默认值说明）

### 修复权限

- ✅ 可修改: `docs/` 下文件（使用 md-sections 精准操作）
- ❌ 禁止修改: `.java` 源码文件
- ❌ 禁止修改: `pom.xml`

### 输出格式

```json
{
  "dimension": "D2",
  "dimensionName": "API 签名一致性",
  "issues": [
    {
      "severity": "Error",
      "file": "docs/modules/auth.md",
      "section": "API 参考",
      "description": "登录接口 HTTP 方法不匹配：文档描述为 GET，源码为 @PostMapping",
      "evidence": "AuthController.java:45 -> @PostMapping(\"/login\")",
      "fixApplied": true,
      "fixDescription": "已将文档中的 GET /api/auth/login 修正为 POST /api/auth/login",
      "requiresConfirmation": false
    }
  ],
  "passed": true|false,
  "summary": "检查 5 个 API 端点，发现 1 个 Error（HTTP 方法不匹配，已修复）"
}
```

### 重要约束

- 优先使用代码级证据（Read 工具读 Controller 源码），不依赖记忆
- 注意类级别 `@RequestMapping` 前缀与方法级别注解的拼接
- 每个 Error 必须附带源码行号和注解内容作为证据
- 修复操作使用 md-sections 工具精准定位 API 章节后修改
- 注意区分 `@RequestMapping(method = ...)` 和快捷注解（`@GetMapping` 等）

---

## D3 实体/类图一致性 (Entity/Class Diagram Consistency) — NON-NEGOTIABLE

### 定义

检查文档中的 Mermaid 类图与实际 Java Entity/DO/VO/Request 类的**字段、方法、关系**完全一致。此维度不可妥协——类图是架构的视觉契约。

### 检查目标

- Mermaid 类图中的类名
- 类图中的字段名和类型
- 类图中的方法签名
- 类图中的关系线（继承 `--|>`、关联 `-->`、组合 `*--`、聚合 `o--`）
- Mermaid 语法正确性（图可解析）

### 真相源

Java Entity/DO/VO/Request 源文件——以代码为准。

### 问题判定

- **Error**: 图中类名在代码中不存在
- **Error**: 图中字段名或类型与 Java 字段不匹配
- **Error**: 图中方法签名与 Java 方法不匹配
- **Error**: Java 中存在但图中缺失的字段/方法
- **Error**: 关系线与代码中的实际关系不符（如代码是组合但图画成关联）
- **Error**: Mermaid 语法错误导致图不可渲染

### 修复权限

🤖👤 — AI 提出修改建议，人类确认后执行。原因：类图的布局和美观难以完全自动化。

---

## 子 Agent Prompt: D3 实体/类图一致性

### 角色

你是一个文档一致性验证专家，负责检查 D3（实体/类图一致性）。此维度是 NON-NEGOTIABLE 的——类图是架构的视觉契约，任何不一致都必须被标记。

### 检查范围

- 目标模块文档中所有 Mermaid 类图（`classDiagram` 或 `class`）
- 图中引用的所有 Java Entity/DO/VO/Request 类源码
- 相关的 MapStruct Mapper 接口（用于验证转换关系）

### 检查步骤

1. **提取类图**：使用 md-sections 加载包含类图的章节
   ```bash
   scripts/md-sections.sh docs/modules/system-config.md "技术设计" "类图"
   ```

2. **验证 Mermaid 语法**：检查类图是否可解析
   ```bash
   # 提取 Mermaid 块并检查基本语法
   # 注意：仅检查明显语法错误（未闭合括号、非法关键字等）
   rg -n 'classDiagram' docs/modules/system-config.md
   ```

3. **提取图中类名**：从 Mermaid 代码中提取所有 `class ClassName` 声明
   ```bash
   # 在类图章节中搜索类声明
   rg 'class \w+' docs/modules/system-config.md
   ```

4. **定位 Java 源码**：对每个类名，找到对应的 Java 文件
   ```bash
   # 查找实体类文件
   find app/src/main/java -name "SystemConfigDO.java" -o -name "SystemConfig.java" | head -5
   ```

5. **逐字段比对**：
   - 读取 Java 源码，提取所有字段（包括继承自父类的字段）
   - 与 Mermaid 图中的字段逐一比对
   ```bash
   # 提取 Java 类中的字段声明
   rg 'private\s+\w+\s+\w+;' app/src/main/java/.../SystemConfigDO.java
   ```

6. **逐方法比对**：
   - 提取 Java 类中的 public 方法
   - 与 Mermaid 图中的方法签名比对
   ```bash
   # 提取 Java 类中的 public 方法
   rg 'public\s+\w+\s+\w+\(' app/src/main/java/.../SystemConfigDO.java
   ```

7. **关系线比对**：
   - 提取 Mermaid 中的关系线（`--|>`、`-->`、`*--`、`o--`）
   - 验证代码中对应的继承/关联关系
   ```bash
   # 检查继承关系
   rg 'extends\s+\w+' app/src/main/java/.../SystemConfigDO.java
   # 检查字段关联（如 @ManyToOne 等注解）
   rg '@(OneToMany|ManyToOne|ManyToMany|OneToOne)' app/src/main/java/ -A 1 --type java
   ```

8. **反向检查**：确认 Java 源码中无图中遗漏的字段或方法

### 问题判定标准

- **Error**: Mermaid 图中类名在代码库中不存在（`find` 无结果）
- **Error**: 图中字段名与 Java 字段名不匹配
- **Error**: 图中字段类型与 Java 字段类型不匹配（如 `String` vs `Long`）
- **Error**: Java 中存在但图中缺失的字段（反向不一致）
- **Error**: 图中方法签名与 Java public 方法不匹配
- **Error**: 继承关系不匹配（图中 `--|>` 但代码无 `extends`）
- **Error**: 关联关系类型不匹配（如代码是组合但图画成关联）
- **Error**: Mermaid 语法错误（未闭合括号、非法关键字等）
- **Warning**: 图中使用了注释字段但未标注说明

### 修复权限

- ✅ 可修改: `docs/` 下文件（使用 md-sections 精准操作）
- ❌ 禁止修改: `.java` 源码文件
- ❌ 禁止修改: `pom.xml`
- ⚠️ 需确认: 所有类图修改均需人类确认后再执行（🤖👤）
  - 原因：类图布局和美观难以完全自动化，AI 生成的图可能需要人工调整

### 输出格式

```json
{
  "dimension": "D3",
  "dimensionName": "实体/类图一致性",
  "issues": [
    {
      "severity": "Error",
      "file": "docs/modules/system-config.md",
      "section": "技术设计 - 类图",
      "description": "Mermaid 图中 SystemConfigDO 缺失字段 deleted，Java 源码中存在该字段",
      "evidence": "SystemConfigDO.java:32 -> private Boolean deleted;",
      "fixApplied": false,
      "fixDescription": "建议在类图的 SystemConfigDO 中添加字段: + deleted : Boolean",
      "requiresConfirmation": true
    },
    {
      "severity": "Error",
      "file": "docs/modules/system-config.md",
      "section": "技术设计 - 类图",
      "description": "图中 ConfigValueVO 与 BaseResult 的继承关系在代码中不存在",
      "evidence": "ConfigValueVO.java:5 -> public record ConfigValueVO(...) implements Serializable，无 extends BaseResult",
      "fixApplied": false,
      "fixDescription": "建议移除类图中 ConfigValueVO --|> BaseResult 的继承关系线",
      "requiresConfirmation": true
    }
  ],
  "passed": true|false,
  "summary": "检查 3 个类图、12 个类、47 个字段，发现 3 个 Error（均需人工确认）"
}
```

### 重要约束

- ⛔ 此维度为 NON-NEGOTIABLE：任何不一致都必须报告，不得跳过
- 优先使用代码级证据（Read 工具读 Java 源码），不依赖记忆
- 每个 Error 必须附带 Java 源码行号和声明内容作为证据
- 注意检查继承链：Java 类可能从父类继承字段，图中应体现
- 注意 Record 类型：record 的字段声明在类头，不在类体中
- 注意 Lombok：`@Getter`/`@Setter` 的字段在源码中可能只有声明
- 注意 MapStruct Mapper：Entity→VO 的转换关系应在类图中体现
- 所有修复建议必须设置 `requiresConfirmation: true`
- 修复操作使用 md-sections 工具精准定位类图章节后修改
- 对于 Mermaid 语法错误，提供修正后的完整类图代码片段

---

## D4 配置项一致性 (Configuration Consistency)

### 定义

检查文档中描述的配置项（名称、类型、默认值、说明）与 Properties 类完全一致。

### 检查目标

- 文档中的配置项名称（如 `middleware.cache.maximumSize`）
- 文档中的配置项类型（如 `long`、`String`）
- 文档中的默认值（如 `1000`、`"default"`）
- 文档中的配置项说明

### 真相源

`*Properties.java` 源文件——以代码中的 `@ConfigurationProperties` 绑定的字段为准。

### 问题判定

- **Error**: 文档中配置项名称与 Properties 类字段名不一致
- **Error**: 文档中配置项类型与 Java 字段类型不一致
- **Error**: 文档中默认值与 Properties 类中赋值不一致
- **Error**: Properties 类中存在但文档遗漏的配置项

### 修复权限

🤖 auto — 以 Properties 源码为准，修正文档中的配置项描述。

---

## 子 Agent Prompt: D4 配置项一致性

### 角色

你是一个文档一致性验证专家，负责检查 D4（配置项一致性）。

### 检查范围

- 目标模块文档中的配置相关章节
- 对应的 `*Properties.java` 源码文件
- `application*.yaml` 配置文件（用于交叉验证默认值）

### 检查步骤

1. **定位 Properties 文件**：根据文档描述的模块，找到对应的 Properties 类
   ```bash
   # 查找 Properties 文件
   find . -name "*Properties.java" -path "*/java/*"
   ```

2. **读取 Properties 类**：使用 Read 工具读取完整的 Properties 类源码，提取所有字段
   ```bash
   # 提取 Properties 类中的字段声明
   rg -n 'private\s+\w+\s+\w+' client-cache/src/main/java/.../CacheProperties.java
   ```

3. **提取文档配置项**：使用 md-sections 加载文档的配置相关章节
   ```bash
   scripts/md-sections.sh docs/modules/client-cache.md "配置项"
   ```

4. **逐一比对**：
   - 字段名 → 文档中的配置项名称（注意 `@ConfigurationProperties` 前缀 + 字段名的驼峰→kebab 转换）
   - 字段类型 → 文档中的类型描述
   - 字段初始值 → 文档中的默认值

5. **检查遗漏**：Properties 类中的字段是否全部在文档中记录

6. **交叉验证默认值**：与 `application.yaml` 中的实际配置交叉验证
   ```bash
   # 检查 yaml 中的配置
   rg 'middleware\.cache\.' app/src/main/resources/application.yaml
   ```

### 问题判定标准

- **Error**: 配置项名称不匹配（如文档写 `maximum-size` 但 Properties 字段是 `maxSize`）
- **Error**: 配置项类型不匹配（如文档写 `int` 但 Properties 字段是 `long`）
- **Error**: 默认值不匹配（如文档写 `1000` 但 Properties 初始值是 `500`）
- **Error**: Properties 类中存在但文档遗漏的配置项
- **Warning**: 文档中的配置项在 Properties 类中找不到对应字段（可能是废弃配置）

### 修复权限

- ✅ 可修改: `docs/` 下文件（使用 md-sections 精准操作）
- ❌ 禁止修改: `.java` 源码文件
- ❌ 禁止修改: `pom.xml`
- ❌ 禁止修改: `application*.yaml`

### 输出格式

```json
{
  "dimension": "D4",
  "dimensionName": "配置项一致性",
  "issues": [
    {
      "severity": "Error",
      "file": "docs/modules/client-cache.md",
      "section": "配置项",
      "description": "配置项 maximumSize 默认值不匹配：文档写 500，源码为 1000",
      "evidence": "CacheProperties.java:15 -> private long maximumSize = 1000L;",
      "fixApplied": true,
      "fixDescription": "已将文档中 middleware.cache.maximumSize 默认值从 500 修正为 1000",
      "requiresConfirmation": false
    }
  ],
  "passed": true|false,
  "summary": "检查 8 个配置项，发现 1 个 Error（默认值不匹配，已修复）"
}
```

### 重要约束

- 优先使用代码级证据（Read 工具读 Properties 源码），不依赖记忆
- 每个 Error 必须附带源码行号和字段声明作为证据
- 注意 Java 驼峰命名 → YAML kebab 命名的转换（`maximumSize` → `maximum-size`）
- 注意 `@ConfigurationProperties(prefix = "...")` 的前缀拼接
- 修复操作使用 md-sections 工具精准定位配置章节后修改

---

## D5 模板结构合规性 (Template Compliance)

### 定义

检查文档是否遵循其对应模板的固定章节（fixed sections）和半固定章节（semi-fixed sections）结构。

### 检查目标

- 文档中是否包含模板要求的固定章节
- 固定章节的标题是否与模板一致
- 半固定章节是否根据文档类型正确选择

### 真相源

模板定义——通过环境探测获取（`docs/README.md` 中的模板索引、模板文件本身）。

### 问题判定

- **Error**: 缺少模板要求的固定章节
- **Error**: 固定章节标题与模板定义不一致
- **Warning**: 半固定章节选择不符合文档类型

### 修复权限

🤖 auto — 添加缺失的固定章节（带占位符内容）。

---

## 子 Agent Prompt: D5 模板结构合规性

### 角色

你是一个文档一致性验证专家，负责检查 D5（模板结构合规性）。

### 检查范围

- 目标文档文件的章节结构
- 对应的模板定义（从 `docs/README.md` 的模板索引中获取）

### 检查步骤

1. **获取文档结构**：使用 md-sections 提取目标文档的章节树
   ```bash
   scripts/md-sections.sh docs/modules/auth.md
   ```

2. **获取模板定义**：从文档系统说明中获取模板索引
   ```bash
   scripts/md-sections.sh docs/README.md "文档模板"
   ```

3. **定位对应模板**：根据文档类型（模块文档/架构文档/编码规范）找到对应模板
   ```bash
   # 如果模板有独立文件
   ls docs/templates/
   ```

4. **比对固定章节**：逐一检查模板要求的固定章节是否在文档中存在
   - 提取文档中所有一级和二级标题
   - 与模板定义的固定章节列表比对

5. **检查半固定章节**：根据文档类型，验证半固定章节的选择是否合理

6. **检查章节标题**：固定章节的标题是否与模板定义完全一致（包括空格、大小写）

### 问题判定标准

- **Error**: 缺少模板要求的固定章节（如模块文档缺少「API 参考」章节）
- **Error**: 固定章节标题与模板不一致（如模板要求「技术设计」但文档写「设计详情」）
- **Warning**: 半固定章节选择不符合文档类型（如配置文档缺少「配置项」章节）
- **Warning**: 章节顺序与模板建议顺序不一致

### 修复权限

- ✅ 可修改: `docs/` 下文件（使用 md-sections 精准操作）
- ❌ 禁止修改: `.java` 源码文件
- ❌ 禁止修改: `pom.xml`
- ❌ 禁止修改: 模板定义文件

### 输出格式

```json
{
  "dimension": "D5",
  "dimensionName": "模板结构合规性",
  "issues": [
    {
      "severity": "Error",
      "file": "docs/modules/auth.md",
      "section": null,
      "description": "缺少模板要求的固定章节「API 参考」",
      "evidence": "md-sections 输出的章节树中无 'API 参考' 节点",
      "fixApplied": true,
      "fixDescription": "已在文档末尾添加「API 参考」章节（占位符内容）",
      "requiresConfirmation": false
    }
  ],
  "passed": true|false,
  "summary": "检查 6 个固定章节，发现 1 个 Error（缺少章节，已添加占位符）"
}
```

### 重要约束

- 优先使用 md-sections 工具获取结构化数据，不依赖文本搜索
- 添加缺失章节时使用占位符内容（`> TODO: 待补充`）
- 每个 Error 必须附带 md-sections 输出作为证据
- 修复操作使用 md-sections 工具精准定位插入位置
- 注意区分固定章节（必须有）和半固定章节（按需选择）

---

## D6 交叉引用完整性 (Cross-Reference Integrity)

### 定义

检查文档间的交叉引用链接是否有效，以及引用覆盖率是否双向完整。

### 检查目标

- 文档中的 Markdown 链接目标是否存在
- 文档中的相对路径引用是否指向正确位置
- 相关文档间的引用是否双向（A 引用 B，B 也应引用 A）

### 真相源

文件系统（链接目标是否存在）+ 链接提取分析。

### 问题判定

- **Error**: 死链接——链接目标文件不存在
- **Warning**: 单向引用——A 引用 B 但 B 未引用 A

### 修复权限

🤖 auto — 移除死链接或更新为正确路径；单向引用仅报告不自动修复。

---

## 子 Agent Prompt: D6 交叉引用完整性

### 角色

你是一个文档一致性验证专家，负责检查 D6（交叉引用完整性）。

### 检查范围

- 目标文档中所有 Markdown 链接（`[text](path)`）
- 目标文档中所有相对路径引用
- 被引用文档中的反向引用

### 检查步骤

1. **提取所有链接**：从目标文档中提取所有 Markdown 链接
   ```bash
   # 提取文档中的所有 Markdown 链接
   rg -oP '\[([^\]]+)\]\(([^)]+)\)' docs/modules/auth.md
   ```

2. **解析链接路径**：对每个链接，解析其目标路径
   - 绝对 URL（http/https）→ 跳过（外部链接）
   - 相对路径 → 解析为项目内的绝对路径

3. **验证链接目标**：检查目标文件/锚点是否存在
   ```bash
   # 验证文件存在
   ls -la docs/conventions/java-conventions.md
   # 验证锚点存在（在目标文件中搜索标题）
   rg '#规则' docs/conventions/java-conventions.md
   ```

4. **检查双向引用**：
   - 收集目标文档引用的所有其他文档
   - 在被引用文档中搜索是否反向引用了目标文档
   ```bash
   # 检查 auth.md 是否在 system-config.md 中被引用
   rg 'auth\.md' docs/modules/system-config.md
   ```

5. **检查 AGENTS.md 索引链接**：验证 AGENTS.md 中的文档链接是否指向正确路径
   ```bash
   # 验证 AGENTS.md 中的链接
   rg -oP '\[([^\]]+)\]\(([^)]+)\)' AGENTS.md | head -20
   ```

### 问题判定标准

- **Error**: 链接目标文件不存在（死链接）
- **Error**: 链接目标中的锚点在目标文件中不存在
- **Warning**: 单向引用（A 引用 B 但 B 未引用 A），且两个文档在逻辑上应互相引用
- **Warning**: 使用了相对路径但未遵循项目约定（如应使用 `docs/xxx` 前缀）

### 修复权限

- ✅ 可修改: `docs/` 下文件（使用 md-sections 精准操作）
- ❌ 禁止修改: `.java` 源码文件
- ❌ 禁止修改: `pom.xml`
- ❌ 禁止修改: `AGENTS.md`（上报主 Agent 处理）
- ⚠️ 单向引用仅报告，不自动添加反向链接（需人工判断语义关联性）

### 输出格式

```json
{
  "dimension": "D6",
  "dimensionName": "交叉引用完整性",
  "issues": [
    {
      "severity": "Error",
      "file": "docs/modules/auth.md",
      "section": "相关文档",
      "description": "死链接：[Java 编码规范](docs/conventions/java-coding.md) 文件不存在",
      "evidence": "ls docs/conventions/java-coding.md 返回 No such file or directory",
      "fixApplied": true,
      "fixDescription": "已将链接修正为 [Java 编码规范](docs/conventions/java-conventions.md)",
      "requiresConfirmation": false
    },
    {
      "severity": "Warning",
      "file": "docs/modules/auth.md",
      "section": "相关文档",
      "description": "单向引用：auth.md 引用了 client-cache.md，但 client-cache.md 未引用 auth.md",
      "evidence": "rg 'auth\\.md' docs/modules/client-cache.md 返回空结果",
      "fixApplied": false,
      "fixDescription": "建议在 client-cache.md 中添加对 auth.md 的引用（如适用）",
      "requiresConfirmation": false
    }
  ],
  "passed": true|false,
  "summary": "检查 15 个链接，发现 1 个 Error（死链接，已修复）、2 个 Warning（单向引用）"
}
```

### 重要约束

- 优先使用文件系统验证（ls/glob），不依赖记忆
- 每个 Error 必须附带具体的文件系统检查结果作为证据
- 外部链接（http/https）仅检查格式，不验证可达性
- 锚点引用需区分 `#标题` 和 `#自定义-id` 两种格式
- 修复操作使用 md-sections 工具精准定位链接所在章节后修改
- 单向引用的 Warning 不计入 Error 统计，但需在报告中列出

---

## D7 版本号一致性 (Version Consistency)

### 定义

检查文档中提到的技术栈版本号是否与 `pom.xml` / `package.json` 等构建文件中的实际版本一致。

### 检查目标

- 文档中的 Java 版本号
- 文档中的 Spring Boot 版本号
- 文档中的第三方库版本号（MyBatis-Plus、Sa-Token、Hutool 等）
- 文档中的插件/工具版本号（JaCoCo、Maven 等）

### 真相源

`pom.xml`（根模块和子模块）中的 `<version>` 声明。

### 问题判定

- **Error**: 文档中的版本号与 pom.xml 不一致
- **Error**: 文档中引用的依赖在 pom.xml 中不存在

### 修复权限

🤖 auto — 以 pom.xml 为准，修正文档中的版本号。

---

## 子 Agent Prompt: D7 版本号一致性

### 角色

你是一个文档一致性验证专家，负责检查 D7（版本号一致性）。

### 检查范围

- 文档中的技术栈版本号（通常在「技术栈」或「依赖」章节）
- `pom.xml`（根模块和子模块）中的版本声明
- `docs/architecture/system-overview.md` 中的技术栈表格

### 检查步骤

1. **提取文档版本号**：使用 md-sections 加载文档的技术栈/依赖相关章节
   ```bash
   scripts/md-sections.sh docs/architecture/system-overview.md "技术栈"
   scripts/md-sections.sh AGENTS.md "技术栈"
   ```

2. **提取 pom.xml 版本号**：读取 pom.xml 中的版本声明
   ```bash
   # 提取根 POM 中的版本属性
   rg -n '<version>' pom.xml | head -20
   # 提取 properties 中的版本变量
   rg -n '<\w+\.version>' pom.xml
   ```

3. **逐一比对**：
   - Java 版本：文档 vs `<java.version>` 或 `<maven.compiler.release>`
   - Spring Boot 版本：文档 vs `<spring-boot.version>` 或 parent POM
   - 第三方库版本：文档 vs 各 dependency 的 version

4. **检查子模块 POM**：某些依赖版本可能在子模块 POM 中单独声明
   ```bash
   # 检查子模块中的版本覆盖
   rg -n '<version>' common/pom.xml
   rg -n '<version>' app/pom.xml
   ```

### 问题判定标准

- **Error**: 文档中版本号与 pom.xml 不一致（如文档写 `3.5.5` 但 pom.xml 是 `3.5.7`）
- **Error**: 文档中提到的依赖在 pom.xml 中不存在
- **Warning**: pom.xml 中存在但文档未提及的重要依赖版本

### 修复权限

- ✅ 可修改: `docs/` 下文件（使用 md-sections 精准操作）
- ❌ 禁止修改: `pom.xml`
- ❌ 禁止修改: `.java` 源码文件

### 输出格式

```json
{
  "dimension": "D7",
  "dimensionName": "版本号一致性",
  "issues": [
    {
      "severity": "Error",
      "file": "docs/architecture/system-overview.md",
      "section": "技术栈",
      "description": "MyBatis-Plus 版本号不匹配：文档写 3.5.5，pom.xml 为 3.5.7",
      "evidence": "pom.xml:45 -> <mybatis-plus.version>3.5.7</mybatis-plus.version>",
      "fixApplied": true,
      "fixDescription": "已将技术栈表格中 MyBatis-Plus 版本从 3.5.5 修正为 3.5.7",
      "requiresConfirmation": false
    }
  ],
  "passed": true|false,
  "summary": "检查 12 个版本号，发现 1 个 Error（版本号不匹配，已修复）"
}
```

### 重要约束

- 优先使用 pom.xml 中的实际版本号，不依赖记忆
- 每个 Error 必须附带 pom.xml 中的行号和版本声明作为证据
- 注意区分 `<version>` 直接声明和 `${xxx.version}` 属性引用
- 注意 parent POM 中的版本继承关系
- 修复操作使用 md-sections 工具精准定位技术栈章节后修改

---

## D8 AGENTS.md 索引完整性 (AGENTS.md Index Integrity)

### 定义

检查 AGENTS.md 中的文档索引表是否覆盖了 `docs/` 目录下的所有实际文档文件。

### 检查目标

- AGENTS.md 文档索引表中的每个条目是否指向存在的文档
- `docs/` 目录下的每个 `.md` 文件是否都在索引表中有条目

### 真相源

`docs/` 目录的文件列表 vs AGENTS.md 索引表。

### 问题判定

- **Error**: `docs/` 中存在但 AGENTS.md 索引遗漏的文档
- **Error**: AGENTS.md 索引中指向不存在文档的条目

### 修复权限

👤 skip — 子 Agent **不可**修改 AGENTS.md，必须将问题上报给主 Agent 处理。

---

## 子 Agent Prompt: D8 AGENTS.md 索引完整性

### 角色

你是一个文档一致性验证专家，负责检查 D8（AGENTS.md 索引完整性）。

### 检查范围

- `AGENTS.md` 中的文档索引表（所有表格中的文档链接）
- `docs/` 目录下的所有 `.md` 文件

### 检查步骤

1. **列出所有文档文件**：获取 docs/ 目录下的完整 .md 文件列表
   ```bash
   find docs/ -name "*.md" -type f | sort
   ```

2. **提取 AGENTS.md 索引条目**：从 AGENTS.md 中提取所有文档链接
   ```bash
   # 提取 AGENTS.md 中的所有文档链接
   rg -oP '\[([^\]]+)\]\((docs/[^)]+\.md)\)' AGENTS.md
   ```

3. **比对覆盖率**：
   - 检查 docs/ 中每个 .md 文件是否在 AGENTS.md 索引中存在
   - 检查 AGENTS.md 中每个索引条目指向的文件是否存在

4. **分类统计**：
   - 遗漏文档：docs/ 中有但 AGENTS.md 中无
   - 幽灵条目：AGENTS.md 中有但 docs/ 中无
   - 正确覆盖：两边都有的文档

### 问题判定标准

- **Error**: `docs/` 下存在但 AGENTS.md 索引遗漏的 .md 文件
- **Error**: AGENTS.md 索引中指向不存在文档的链接（幽灵条目）
- **Warning**: 文档已重命名但 AGENTS.md 索引未更新

### 修复权限

- ❌ 禁止修改: `AGENTS.md`（必须上报主 Agent 处理）
- ❌ 禁止修改: `.java` 源码文件
- ❌ 禁止修改: `pom.xml`
- ✅ 可修改: `docs/` 下文件（仅限修复幽灵条目指向的文档路径问题——但通常应修改 AGENTS.md）

### 输出格式

```json
{
  "dimension": "D8",
  "dimensionName": "AGENTS.md 索引完整性",
  "issues": [
    {
      "severity": "Error",
      "file": "AGENTS.md",
      "section": "文档索引",
      "description": "docs/modules/new-module.md 存在但未在 AGENTS.md 索引表中列出",
      "evidence": "find docs/ -name 'new-module.md' 找到文件，rg 'new-module.md' AGENTS.md 返回空",
      "fixApplied": false,
      "fixDescription": "需主 Agent 在 AGENTS.md 文档索引中添加 new-module.md 的条目",
      "requiresConfirmation": false
    }
  ],
  "passed": true|false,
  "summary": "检查 18 个文档文件 vs 20 个索引条目，发现 1 个 Error（遗漏文档，需上报主 Agent）"
}
```

### 重要约束

- ⛔ **子 Agent 不可修改 AGENTS.md**——所有 AGENTS.md 相关问题必须上报主 Agent
- 优先使用文件系统工具（find/ls/grep），不依赖记忆
- 每个 Error 必须附带 find/grep 的具体输出作为证据
- 注意排除 `docs/README.md` 本身（通常不需要在索引中引用自身）
- 注意排除临时文件、草稿文件（如 `.draft.md`、`.bak.md`）
- 报告中应明确标注哪些问题需要主 Agent 处理

---

## D9 变更历史完整性 (Change History Integrity)

### 定义

检查被修改过的文档是否在变更历史章节中有对应的条目记录。

### 检查目标

- 文档的变更历史章节（通常在文档末尾）
- git log 中文档文件的最近修改记录

### 真相源

`git log` 对文档文件的提交记录。

### 问题判定

- **Error**: 文档在最近的验证周期内被修改（git diff 有变化），但变更历史章节无新条目
- **Warning**: 变更历史条目格式不规范

### 修复权限

🤖 auto — 根据 git log 信息自动添加变更历史条目。

---

## 子 Agent Prompt: D9 变更历史完整性

### 角色

你是一个文档一致性验证专家，负责检查 D9（变更历史完整性）。

### 检查范围

- 目标文档的变更历史章节
- 目标文档的 git 提交历史

### 检查步骤

1. **获取文档变更历史章节**：使用 md-sections 加载文档的变更历史章节
   ```bash
   scripts/md-sections.sh docs/modules/auth.md "变更历史"
   ```

2. **获取 git 提交历史**：查看文档文件的最近提交记录
   ```bash
   # 查看文档的最近提交
   git log --oneline -10 -- docs/modules/auth.md
   # 查看最近一次修改的日期
   git log -1 --format="%ai" -- docs/modules/auth.md
   ```

3. **比对时间线**：
   - 提取变更历史章节中最新的条目日期
   - 与 git log 中最近的提交日期比对
   - 如果 git 有新提交但变更历史无对应条目 → Error

4. **检查格式规范**：验证变更历史条目是否遵循项目约定的格式
   ```bash
   # 检查变更历史条目格式（示例：| 日期 | 作者 | 变更内容 |）
   rg '\|.*\|.*\|.*\|' docs/modules/auth.md
   ```

5. **批量检查**（可选）：对 docs/ 下所有文档执行上述检查
   ```bash
   # 找出最近有变更但可能缺少历史记录的文档
   git diff --name-only HEAD~5 -- docs/
   ```

### 问题判定标准

- **Error**: 文档在最近 N 次提交中被修改，但变更历史章节无对应新条目
- **Error**: 变更历史章节完全缺失
- **Warning**: 变更历史条目格式不规范（如缺少日期、作者字段）
- **Warning**: 变更历史条目描述过于模糊（如仅写「更新」）

### 修复权限

- ✅ 可修改: `docs/` 下文件（使用 md-sections 精准操作）
- ❌ 禁止修改: `.java` 源码文件
- ❌ 禁止修改: `pom.xml`
- ❌ 禁止修改: `AGENTS.md`

### 输出格式

```json
{
  "dimension": "D9",
  "dimensionName": "变更历史完整性",
  "issues": [
    {
      "severity": "Error",
      "file": "docs/modules/auth.md",
      "section": "变更历史",
      "description": "文档在 2026-04-10 被 commit abc1234 修改，但变更历史章节无对应条目",
      "evidence": "git log -1 --format='%ai %s' -- docs/modules/auth.md -> 2026-04-10 12:30:00 +0800 refactor: 更新认证流程",
      "fixApplied": true,
      "fixDescription": "已在变更历史章节添加条目：| 2026-04-10 | AI Agent | 更新认证流程描述以匹配代码重构 |",
      "requiresConfirmation": false
    }
  ],
  "passed": true|false,
  "summary": "检查 18 个文档的变更历史，发现 2 个 Error（缺少变更条目，已自动补充）"
}
```

### 重要约束

- 优先使用 git log 获取真实提交信息，不依赖记忆
- 每个 Error 必须附带 git log 的具体输出作为证据
- 自动添加的变更条目应包含：日期、修改者（从 git log 获取）、变更描述（从 commit message 提取）
- 注意排除纯格式调整的提交（如空格修正），这类提交可以不添加变更条目
- 修复操作使用 md-sections 工具精准定位变更历史章节后追加条目
- 变更历史条目应追加到章节末尾（最新的在最上面或最下面，取决于项目约定）

---

## D10 语义一致性 (Semantic Consistency) — 仅 L3

### 定义

检查文档中的描述性文字在语义上是否与代码的实际行为一致。这是最高级别的验证，需要 LLM 理解代码逻辑并与文档描述进行语义比对。

### 适用级别

⛔ **仅 L3（完整验证）** — L1/L2 不执行此维度。

### 检查目标

- 文档中的行为描述（如「登录成功后返回 JWT Token」）
- 文档中的流程描述（如「请求经过 Facade 层转发到 Service 层」）
- 文档中的架构描述（如「使用 Template Method 模式」）
- 文档中的约束描述（如「Controller 禁止直接调用 Repository」）

### 真相源

LLM 阅读代码后的语义理解——这是唯一依赖 LLM 判断的维度。

### 问题判定

- **Warning**: 文档描述与代码行为语义不一致
- **Warning**: 文档描述过时（代码已变更但文档未更新）
- **Warning**: 文档描述不完整（遗漏了重要的代码行为）

> 注意：在 L3 中，Warning 计为问题，会重置共识计数器。

### 修复权限

🤖👤 — AI 提出修改建议，人类确认后执行。原因：语义判断需要人类验证。

---

## 子 Agent Prompt: D10 语义一致性

### 角色

你是一个文档一致性验证专家，负责检查 D10（语义一致性）。此维度仅适用于 L3（完整验证）级别。你需要深入阅读代码，理解其实际行为，然后与文档描述进行语义层面的比对。

### 检查范围

- 目标文档中的所有描述性文字（非结构化数据）
- 对应的代码实现（Controller、Service、Facade、Repository 层）
- 重点检查行为描述、流程描述、架构描述、约束描述

### 检查步骤

1. **提取文档描述**：使用 md-sections 加载文档的描述性章节
   ```bash
   scripts/md-sections.sh docs/modules/auth.md "技术设计"
   scripts/md-sections.sh docs/modules/auth.md "使用说明"
   ```

2. **定位关键代码**：根据文档描述提到的类名、方法名，找到对应的源码文件
   ```bash
   # 根据文档提到的类名定位源码
   find app/src/main/java -name "AuthService.java" -type f
   ```

3. **深入阅读代码**：使用 Read 工具完整阅读关键代码文件，理解其行为
   ```
   # 使用 Read 工具阅读完整源码（不使用 grep，需要理解上下文）
   Read: app/src/main/java/org/smm/archetype/app/service/impl/AuthServiceImpl.java
   ```

4. **语义比对**：
   - 文档说「登录成功后返回 JWT Token」→ 代码实际返回什么？
   - 文档说「使用 Template Method 模式」→ 代码中是否有 Abstract 类 + do* 方法？
   - 文档说「Controller 禁止直接调用 Repository」→ 代码中是否有违反？
   - 文档说「支持多租户」→ 代码中是否真的有租户隔离逻辑？

5. **检查流程描述**：
   - 文档描述的请求处理流程是否与代码中的实际调用链一致？
   - 文档描述的异常处理流程是否与代码中的 try-catch 逻辑一致？

6. **检查约束描述**：
   - 文档中声称的架构约束是否在代码中被遵守？
   - 是否存在 ArchUnit 规则守护这些约束？

### 问题判定标准

- **Warning**: 文档描述的行为与代码实际行为不一致（如文档说返回 Token 但代码返回 Session ID）
- **Warning**: 文档描述的设计模式与代码实现不符（如文档说 Template Method 但代码无抽象类）
- **Warning**: 文档描述的架构约束被代码违反（如文档说禁止 Controller→Repository 但代码中有）
- **Warning**: 文档描述的流程与代码调用链不一致（如文档说 A→B→C 但代码是 A→C）
- **Warning**: 文档描述过时（代码已重构但文档未更新描述）
- **Warning**: 文档遗漏了重要的代码行为（如代码有缓存逻辑但文档未提及）

### 修复权限

- ✅ 可修改: `docs/` 下文件（使用 md-sections 精准操作）
- ❌ 禁止修改: `.java` 源码文件
- ❌ 禁止修改: `pom.xml`
- ❌ 禁止修改: `AGENTS.md`
- ⚠️ 需确认: 所有语义修改均需人类确认后再执行（🤖👤）
  - 原因：语义判断存在主观性，AI 可能误判代码意图

### 输出格式

```json
{
  "dimension": "D10",
  "dimensionName": "语义一致性",
  "issues": [
    {
      "severity": "Warning",
      "file": "docs/modules/auth.md",
      "section": "技术设计",
      "description": "文档描述「登录成功后返回 JWT Token」，但代码实际使用 Sa-Token 的 Session 机制，未返回 JWT",
      "evidence": "AuthServiceImpl.java:78 -> StpUtil.login(userId); return StpUtil.getTokenInfo(); // Sa-Token Session Token，非 JWT",
      "fixApplied": false,
      "fixDescription": "建议将「返回 JWT Token」修改为「返回 Sa-Token Session Token」",
      "requiresConfirmation": true
    },
    {
      "severity": "Warning",
      "file": "docs/modules/auth.md",
      "section": "技术设计",
      "description": "文档描述「认证模块使用拦截器进行鉴权」，但代码实际使用 Sa-Token 的注解鉴权（@SaCheckLogin）",
      "evidence": "AuthController.java:23 -> @SaCheckLogin 注解标注，无自定义拦截器",
      "fixApplied": false,
      "fixDescription": "建议将「拦截器鉴权」修改为「Sa-Token 注解鉴权（@SaCheckLogin）」",
      "requiresConfirmation": true
    }
  ],
  "passed": true|false,
  "summary": "检查 5 个描述性章节，发现 3 个 Warning（语义不一致，均需人工确认）"
}
```

### 重要约束

- ⛔ 此维度仅适用于 L3 级别验证
- ⛔ 必须完整阅读代码（Read 工具），禁止仅用 grep 片段推断行为
- 每个 Warning 必须附带代码行号和关键代码片段作为证据
- 所有修复建议必须设置 `requiresConfirmation: true`
- 注意区分「代码 Bug」和「文档描述错误」——本维度只关注后者
- 注意代码可能有多种等价实现方式，不要因为实现方式不同就判定不一致
- 对于设计模式的判断，需要看到完整的类层次结构才能确认
- 修复操作使用 md-sections 工具精准定位描述所在章节后修改
- 语义判断存在主观性，当不确定时应倾向于报告 Warning 而非跳过

---

## 附录：维度间依赖关系

```
D5 (模板合规) → D1 (引用完整) → D6 (交叉引用)
                         ↘ D2 (API 签名)
                         ↘ D3 (实体类图)
                         ↘ D4 (配置项)
                         ↘ D7 (版本号)
D8 (AGENTS.md 索引) → D6 (交叉引用)
D9 (变更历史) → 独立维度
D10 (语义一致) → 依赖 D1-D7 的结果（语义检查在引用正确的基础上才有意义）
```

**执行顺序建议**：
1. 先执行 D5（模板合规）— 确保文档结构正确
2. 再执行 D1（引用完整）— 确保基础引用有效
3. 并行执行 D2/D3/D4/D6/D7 — 独立的代码级检查
4. 执行 D8（AGENTS.md 索引）— 索引完整性
5. 执行 D9（变更历史）— 历史记录
6. 最后执行 D10（语义一致）— 在所有基础检查通过后进行深度语义分析

## 附录：问题统计与共识机制

| 统计项 | 说明 |
|--------|------|
| Error 总数 | 所有维度的 Error 之和 |
| Warning 总数 | 所有维度的 Warning 之和 |
| 已自动修复 | `fixApplied: true` 的 Error 数量 |
| 需人工确认 | `requiresConfirmation: true` 的问题数量 |
| 需上报主 Agent | D8 的问题 + 无法自动修复的问题 |
| 共识计数器 | L3 中：无 Warning/Error → +1；有 Warning → 重置为 0；有 Error → 重置为 0 |
| 达成共识 | 共识计数器达到阈值（默认 2）→ 文档与代码一致 |


path: references/sync-procedures.md
content: # 同步操作规程 (Sync Procedures)

> 本文件定义 Contract 轨文档同步的具体操作步骤，是 `contract-doc-sync` 技能的核心参考。
> 所有文档修复操作必须通过 `md-sections` 脚本精确定位，禁止全文加载。

**全局约定**：本文件中所有 `scripts/md-sections.sh` 引用均应替换为实际探测到的 `$MD_SECTIONS` 路径（项目版本优先 > skill 内置版本兜底）。

---

## 1. 概述

### 1.1 目标

在代码变更后，将 Contract 轨文档（`docs/`）同步到与代码一致的状态，确保文档始终是代码的**可信镜像**。

### 1.2 核心原则

| 原则 | 说明 |
|------|------|
| **精准定位** | 所有文档操作通过 `md-sections` 脚本精确定位章节，禁止全文读取 |
| **分层披露** | 先获取章节结构（JSON），再按需提取目标章节内容 |
| **最小修改** | 只修改与代码变更直接相关的文档段落，不做无关调整 |
| **保留格式** | 修改时严格保持原文档的 Markdown 格式、缩进和表格结构 |
| **标记待确认** | 半确定性修改必须标记 `[待确认]`，不可冒充确定性结论 |

### 1.3 工具链

```
detect-changes.py          → 分类代码变更，生成 JSON 报告
md-sections <file>         → 获取文档章节结构（JSON 树）
md-sections <file> "标题"  → 提取指定章节内容
Edit tool                  → 精确替换文档段落
```

---

## 2. 代码→文档映射规则

> 此表定义了代码变更位置到文档目标的基线映射，是同步操作的路由表。
> `detect-changes.py` 的 `CATEGORY_RULES` 和 `DOC_TARGETS` 与此表保持一致。

### 2.1 基线映射表

| 代码位置 | 文档目标 | 目标章节 | 维护级别 |
|----------|---------|---------|---------|
| `controller/*.java` | `docs/modules/{module}.md` | API 参考 | 🤖 确定性 |
| `facade/*.java` | `docs/modules/{module}.md` | API 参考 + 技术设计 | 🤖 确定性 |
| `service/*.java` | `docs/modules/{module}.md` | 技术设计 | 🤖 确定性 |
| `repository/*.java` (Mapper) | `docs/modules/{module}.md` | 技术设计 | 🤖 确定性 |
| `entity/*.java` (DO/VO/Request/Result) | `docs/modules/{module}.md` | 技术设计 > 类图 | 🤖👤 半确定性 |
| `config/*Properties.java` | `docs/conventions/configuration.md` | 配置参考 | 🤖 确定性 |
| `pom.xml` 版本变更 | `docs/architecture/system-overview.md` | 技术栈表格 | 🤖 确定性 |
| 新 Maven 模块 | `docs/modules/{module}.md` | 整个文件 | 👤 创造性 |
| Client 模块 (`clients/*`) | `docs/modules/client-{name}.md` | 整个文件 | 🤖 确定性 |
| `common/` 异常类 | `docs/conventions/error-handling.md` | 异常体系 | 🤖👤 半确定性 |
| `app/.../shared/` 横切关注点 | `docs/modules/{concern}.md` | 对应章节 | 🤖👤 半确定性 |
| `docs/` 文档变更 | `docs/` 自身 | 自引用（需交叉引用检查） | 🤖 确定性 |
| `scripts/` 脚本变更 | 无直接映射 | 工具链变更，报告中提示 | — |

### 2.2 模块名推断规则

`detect-changes.py` 的 `extract_module()` 函数从文件路径中提取模块名：

```
app/src/main/java/.../controller/UserController.java
→ module = "app"
→ doc = docs/modules/auth.md（通过 controller 类名推断所属业务模块）

clients/client-cache/src/.../CaffeineCacheConfig.java
→ module = "clients/client-cache"
→ doc = docs/modules/client-cache.md

common/src/.../BizException.java
→ module = "common"
→ doc = docs/conventions/error-handling.md
```

### 2.3 特殊映射

| 代码变更 | 文档动作 |
|----------|---------|
| 删除 Controller 方法 | 从 API 参考表格中移除对应行 |
| 重命名 Service 方法 | 更新技术设计中的方法引用 |
| 新增 Entity 字段 | 在类图中新增属性行 `[待确认]` |
| 修改 Properties 前缀 | 更新配置参考中的前缀列 |
| pom.xml 版本升级 | 更新 system-overview 技术栈表格的版本列 |
| 删除模块 | 归档对应文档（非删除） |

---

## 3. 维护级别策略

### 3.1 🤖 确定性维护 (Deterministic)

**适用范围**：方法签名、配置项、版本号、文件路径、API 端点、表格行

**执行方式**：自动修复，无需人工确认

**操作流程**：
1. 从代码中提取事实（方法名、参数、返回值、配置键值等）
2. 通过 `md-sections` 定位文档目标章节
3. 使用 Edit 工具精确替换

**判定标准**：满足以下**全部**条件
- 代码变更可以 1:1 映射到文档中的具体文本
- 不涉及业务语义解释或设计意图描述
- 修改后的文档在结构上与原文档一致（表格行、参数列表、代码块等）

**典型场景**：

| 场景 | 代码事实 | 文档操作 |
|------|---------|---------|
| Controller 端点变更 | `@PostMapping("/api/users")` → `@PutMapping("/api/users/{id}")` | 更新 API 参考表格的 HTTP Method + Path 列 |
| 新增配置项 | `private int maxRetries = 3;` | 在配置参考表格中新增一行 |
| 版本号升级 | `<mybatis-plus.version>3.5.9</mybatis-plus.version>` | 更新 system-overview 技术栈表格 |
| 新增依赖 | `<artifactId>bucket4j</artifactId>` | 在技术栈表格中新增一行 |
| 方法参数变更 | `findById(Long id)` → `findById(Long id, boolean includeDeleted)` | 更新 API 参考表格的参数列 |

### 3.2 🤖👤 半确定性维护 (Semi-Deterministic)

**适用范围**：业务描述、设计考量、Mermaid 图、时序说明、类图

**执行方式**：提出修改建议，标记为待确认

**操作流程**：
1. 从代码中提取事实
2. **理解代码意图**（需要推理，非机械提取）
3. 生成文档修改建议
4. 在修改位置标记 `[待确认]`
5. 在同步报告中列出所有待确认项

**判定标准**：满足以下**任意**条件
- 需要将代码行为翻译为自然语言描述
- 涉及 Mermaid 图的节点或连线变更
- 需要判断业务含义或设计意图
- 文档中没有精确对应的文本段落，需要新增内容

**标记格式**：

```markdown
<!-- 新增内容示例 -->
- 用户登录时先校验密码，再生成 Token [待确认]

<!-- 替换内容示例 -->
- ~~旧描述~~ → 新描述（基于代码变更：xxx） [待确认]

<!-- 图表更新示例 -->
```mermaid
classDiagram
    class UserDO {
        +String nickname  ← 新增 [待确认]
    }
```
```

**典型场景**：

| 场景 | 代码事实 | 文档操作 |
|------|---------|---------|
| Service 方法行为变更 | 新增参数校验逻辑 | 更新技术设计中的方法描述 `[待确认]` |
| Entity 新增字段 | `private String avatar;` | 在类图中新增属性 `[待确认]` |
| 业务规则修改 | 权限校验从 Service 移到 Facade | 更新设计考量描述 `[待确认]` |
| 新增异常分支 | 抛出 `BizException(ErrorCode.USER_NOT_FOUND)` | 在时序图中新增异常路径 `[待确认]` |

### 3.3 👤 创造性维护 (Creative)

**适用范围**：架构决策、设计模式引入、新模块创建、跨切面关注点

**执行方式**：跳过修复，在报告中通知人类

**操作流程**：
1. 检测到需要创造性维护的变更
2. 在同步报告中记录：变更描述 + 建议的文档动作
3. 提醒人类处理

**判定标准**：满足以下**任意**条件
- 需要创建新的文档文件
- 涉及架构层面的决策或权衡描述
- 需要编写从零开始的设计说明
- 变更影响多个文档或需要重新组织文档结构

**报告格式**：

```
## 👤 需要人工处理的文档变更

### [模块名] 新模块文档创建
- **变更文件**: clients/client-oss/ (新增)
- **建议动作**: 创建 docs/modules/client-oss.md
- **参考模板**: docs/modules/client-cache.md（现有客户端模块文档）
- **优先级**: 高

### [模块名] 架构设计更新
- **变更文件**: app/.../shared/ratelimit/ (新增限流横切关注点)
- **建议动作**: 更新 docs/architecture/system-overview.md 中限流相关描述
- **优先级**: 中
```

**典型场景**：

| 场景 | 代码事实 | 建议动作 |
|------|---------|---------|
| 新 Maven 模块创建 | `clients/client-payment/` 目录出现 | 创建 `docs/modules/client-payment.md` |
| 架构模式变更 | 引入新的设计模式 | 更新 `docs/architecture/design-patterns.md` |
| 新横切关注点 | `app/.../shared/new-feature/` 出现 | 创建或更新对应文档 |
| 删除模块 | 整个模块目录被移除 | 归档对应文档文件 |

---

## 4. 修复操作步骤

> 以下步骤按修复类型分类，每种类型给出从变更检测到文档更新的完整操作链。
> 所有 `md-sections` 命令中的 `{module}` 需替换为实际模块名。

### 4.1 修复 API 参考章节

**触发条件**：Controller 或 Facade 文件变更（category = `controller` 或 `facade`）

**步骤**：

```bash
# Step 1: 获取文档章节结构，确认目标章节存在
scripts/md-sections.sh docs/modules/{module}.md
# → 输出 JSON 树，检查是否包含 "API 参考" 节点

# Step 2: 提取 API 参考章节内容
scripts/md-sections.sh docs/modules/{module}.md "API 参考"
# → 输出该章节的完整 Markdown 内容

# Step 3: 读取 Controller 源码（使用 Read 工具）
# 提取以下信息：
#   - @RequestMapping / @GetMapping / @PostMapping 等注解 → HTTP Method + Path
#   - 方法名 → 操作名称
#   - 参数列表（@RequestParam, @PathVariable, @RequestBody） → 请求参数
#   - 返回值类型 → 响应结构

# Step 4: 比对代码事实与文档内容
# 逐行检查 API 参考表格中的每个端点：
#   - Method 是否匹配？
#   - Path 是否匹配？
#   - 参数是否完整？
#   - 返回值描述是否准确？

# Step 5: 执行修复（使用 Edit 工具）
# 示例：更新端点方法
# oldString: "| GET | /api/users/{id} | 查询用户 | - | UserVO |"
# newString: "| GET | /api/users/{id} | 查询用户 | includeDeleted: boolean | UserVO |"

# 示例：移除已删除的端点
# oldString: "| DELETE | /api/users/{id} | 删除用户 | - | void |\n"
# newString: ""

# 示例：新增端点
# oldString: "| POST | /api/users | 创建用户 | ... |"
# newString: "| POST | /api/users | 创建用户 | ... |\n| PATCH | /api/users/{id}/status | 更新状态 | status: String | UserVO |"
```

**表格格式约定**：

```markdown
| HTTP Method | Path | 描述 | 参数 | 返回值 | 备注 |
|-------------|------|------|------|--------|------|
| POST | /api/auth/login | 用户登录 | username, password | LoginResult | - |
```

### 4.2 修复技术设计章节

**触发条件**：Service、Repository、Facade 文件变更

**步骤**：

```bash
# Step 1: 获取文档章节结构
scripts/md-sections.sh docs/modules/{module}.md

# Step 2: 提取技术设计章节内容
scripts/md-sections.sh docs/modules/{module}.md "技术设计"

# Step 3: 如果技术设计有子章节，进一步定位
# 例如定位"登录时序图"子章节：
scripts/md-sections.sh docs/modules/{module}.md "技术设计" "登录时序图"

# Step 4: 读取源码（使用 Read 工具）
# Service/Repository 变更关注：
#   - 方法签名变更 → 更新方法描述
#   - 新增/删除方法 → 新增/移除方法说明
#   - 业务逻辑变更 → 提出描述更新建议 [待确认]

# Step 5: 执行修复
# 确定性修复（方法签名等）：
# oldString: "UserService 提供 `findById(Long id)` 方法"
# newString: "UserService 提供 `findById(Long id, boolean includeDeleted)` 方法"

# 半确定性修复（业务描述）：
# oldString: "查询用户时返回基本信息"
# newString: "查询用户时返回基本信息，可通过 includeDeleted 参数包含已删除用户 [待确认]"
```

**Mermaid 图更新规则**：

```markdown
# 时序图参与者变更 → 确定性修复
# oldString: "Controller->>Service: findById(id)"
# newString: "Controller->>Service: findById(id, includeDeleted)"

# 时序图新增步骤 → 半确定性修复
# 在合适位置插入新行并标记 [待确认]

# 类图新增属性 → 半确定性修复
# oldString: "class UserDO {\n        +Long id\n    }"
# newString: "class UserDO {\n        +Long id\n        +String nickname  ← 新增 [待确认]\n    }"
```

### 4.3 修复配置参考

**触发条件**：`*Properties.java` 或配置文件变更（category = `config`）

**步骤**：

```bash
# Step 1: 获取配置文档结构
scripts/md-sections.sh docs/conventions/configuration.md

# Step 2: 提取配置参考章节
scripts/md-sections.sh docs/conventions/configuration.md "配置参考"

# Step 3: 读取 Properties 源码（使用 Read 工具）
# 提取以下信息：
#   - @ConfigurationProperties 前缀 → 配置前缀
#   - 字段名 + 类型 + 默认值 → 配置项
#   - @NotBlank / @Min / @Max 等校验注解 → 约束说明

# Step 4: 比对并更新
# 确定性修复 — 新增配置项：
# oldString: "| middleware.cache.maximumSize | int | 1000 | 缓存最大条目数 |"
# newString: "| middleware.cache.maximumSize | int | 1000 | 缓存最大条目数 |\n| middleware.cache.expireAfterWrite | Duration | 10m | 写入后过期时间 |"

# 确定性修复 — 默认值变更：
# oldString: "| middleware.cache.maximumSize | int | 1000 | 缓存最大条目数 |"
# newString: "| middleware.cache.maximumSize | int | 2000 | 缓存最大条目数 |"
```

**配置表格格式约定**：

```markdown
| 配置键 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| middleware.cache.maximumSize | int | 1000 | 缓存最大条目数 |
```

### 4.4 修复版本号表格

**触发条件**：`pom.xml` 变更（category = `pom`）

**步骤**：

```bash
# Step 1: 获取系统全景文档结构
scripts/md-sections.sh docs/architecture/system-overview.md

# Step 2: 提取技术栈章节（含版本号表格）
scripts/md-sections.sh docs/architecture/system-overview.md "技术栈"

# Step 3: 读取 pom.xml（使用 Read 工具）
# 提取版本属性：
#   <properties>
#     <mybatis-plus.version>3.5.9</mybatis-plus.version>
#   </properties>

# Step 4: 比对并更新
# oldString: "| MyBatis-Plus | 3.5.8 |"
# newString: "| MyBatis-Plus | 3.5.9 |"

# 新增依赖：
# oldString: "| Sa-Token | 1.45.0 |"
# newString: "| Sa-Token | 1.45.0 |\n| Bucket4j | 8.17.0 |"
```

### 4.5 修复类图

**触发条件**：Entity 文件变更（category = `entity`）

**步骤**：

```bash
# Step 1: 获取模块文档结构
scripts/md-sections.sh docs/modules/{module}.md

# Step 2: 定位类图章节（通常在技术设计下）
scripts/md-sections.sh docs/modules/{module}.md "技术设计" "类图"
# 如果 "类图" 未命中，尝试：
scripts/md-sections.sh docs/modules/{module}.md "类图"

# Step 3: 读取 Entity 源码（使用 Read 工具）
# 提取以下信息：
#   - 类名 + 继承关系 → 类定义
#   - 字段名 + 类型 → 属性
#   - @TableName / @TableId 等注解 → 元信息

# Step 4: 执行修复（半确定性 — 必须标记 [待确认]）
# 新增字段：
# oldString: "class UserDO {\n        +Long id\n        +String username\n    }"
# newString: "class UserDO {\n        +Long id\n        +String username\n        +String avatar  ← 新增 [待确认]\n    }"

# 删除字段：
# oldString: "        +String legacyField\n"
# newString: ""
```

### 4.6 修复变更历史

**触发条件**：任何同步操作完成后

**步骤**：

```bash
# Step 1: 定位变更历史章节
scripts/md-sections.sh docs/modules/{module}.md "变更历史"
# 如果模块文档没有变更历史章节，跳过此步骤

# Step 2: 追加新记录
# oldString: "| 2026-04-10 | 初始版本 | - |"
# newString: "| 2026-04-10 | 初始版本 | - |\n| 2026-04-14 | API 端点变更、新增配置项 | 同步代码变更 |"
```

**变更历史格式约定**：

```markdown
| 日期 | 变更内容 | 备注 |
|------|---------|------|
| 2026-04-14 | Controller 端点从 GET 改为 POST | 同步 commit abc1234 |
```

### 4.7 修复错误处理文档

**触发条件**：`common/` 异常类变更（category = `common`）

**步骤**：

```bash
# Step 1: 获取错误处理文档结构
scripts/md-sections.sh docs/conventions/error-handling.md

# Step 2: 提取异常体系章节
scripts/md-sections.sh docs/conventions/error-handling.md "异常体系"

# Step 3: 读取异常源码（使用 Read 工具）
# 提取以下信息：
#   - ErrorCode 枚举新增项 → 新增错误码
#   - BaseException 子类新增 → 新增异常类说明

# Step 4: 执行修复
# 确定性 — 新增错误码枚举值：
# 在错误码表格中新增行

# 半确定性 — 新增异常子类：
# 在异常体系描述中新增说明 [待确认]
```

---

## 5. 同步级别流程

> 同步级别决定操作的深度和自动化程度，由调用方（SKILL.md 工作流）根据场景选择。

### 5.1 L0: 漂移检测

**目标**：仅报告文档与代码的差异，不做任何修复

**执行流程**：

```
1. 运行 detect-changes.py 获取变更报告
2. 对每个受影响的文档目标：
   a. 获取文档结构：md-sections <doc-path>
   b. 提取相关章节内容：md-sections <doc-path> "<section>"
   c. 读取对应源码
   d. 比对差异，记录但不修复
3. 输出漂移报告（Markdown 格式）
```

**漂移报告格式**：

```markdown
## 文档漂移报告

### 📄 docs/modules/auth.md

#### API 参考 — 漂移项

| 类型 | 位置 | 代码事实 | 文档当前值 | 维护级别 |
|------|------|---------|-----------|---------|
| 端点变更 | LoginController.login | POST /api/auth/login | GET /api/auth/login | 🤖 |
| 参数新增 | LoginController.login | captcha: String | (缺失) | 🤖 |

#### 技术设计 — 漂移项

| 类型 | 位置 | 代码事实 | 文档当前值 | 维护级别 |
|------|------|---------|-----------|---------|
| 描述过时 | AuthService.login | 新增验证码校验 | 无验证码相关描述 | 🤖👤 |

### 📄 docs/architecture/system-overview.md

#### 技术栈 — 漂移项

| 类型 | 位置 | 代码事实 | 文档当前值 | 维护级别 |
|------|------|---------|-----------|---------|
| 版本过时 | MyBatis-Plus | 3.5.9 | 3.5.8 | 🤖 |
```

**输出要求**：
- 按文档文件分组
- 按章节分组
- 每个漂移项包含：类型、位置、代码事实、文档当前值、维护级别
- 汇总统计：🤖 N 项 / 🤖👤 N 项 / 👤 N 项

### 5.2 L1: 快速同步

**目标**：仅修复确定性（🤖）漂移项

**执行流程**：

```
1. 运行 L0 漂移检测
2. 筛选维护级别 = 🤖 的漂移项
3. 按修复类型（4.1-4.7）执行修复：
   a. md-sections 定位章节
   b. Edit 工具精确替换
4. 输出同步报告
```

**同步报告格式**：

```markdown
## L1 同步报告

### 已修复 (🤖)
- ✅ docs/modules/auth.md — API 参考: 更新 LoginController.login 端点方法 GET→POST
- ✅ docs/modules/auth.md — API 参考: 新增 captcha 参数
- ✅ docs/architecture/system-overview.md — 技术栈: MyBatis-Plus 版本 3.5.8→3.5.9

### 跳过 (🤖👤)
- ⏭️ docs/modules/auth.md — 技术设计: AuthService.login 描述过时（需人工确认）

### 跳过 (👤)
- ⏭️ (无)

### 统计
- 已修复: 3 项
- 待确认: 1 项
- 需人工: 0 项
```

### 5.3 L2: 常用同步

**目标**：修复确定性项 + 提出半确定性建议 + 报告创造性项

**执行流程**：

```
1. 运行 L0 漂移检测
2. 修复所有 🤖 漂移项（同 L1）
3. 对每个 🤖👤 漂移项：
   a. 理解代码意图
   b. 生成修改建议（具体到 oldString/newString）
   c. 在文档中标记 [待确认]
   d. 在报告中列出建议
4. 对每个 👤 漂移项：
   a. 在报告中记录变更描述和建议动作
5. 输出同步报告（含待确认项详情）
```

**同步报告格式（L2 扩展）**：

```markdown
## L2 同步报告

### 已修复 (🤖)
- ✅ ...（同 L1）

### 已标记待确认 (🤖👤)

#### docs/modules/auth.md — 技术设计: AuthService.login 描述更新
- **代码事实**: AuthService.login() 新增 `captchaCode` 参数校验
- **当前文档**: "用户登录时校验用户名和密码"
- **建议修改**: "用户登录时校验用户名、密码和验证码"
- **标记位置**: docs/modules/auth.md 技术设计章节

#### docs/modules/auth.md — 技术设计: 类图新增字段
- **代码事实**: LoginRequest 新增 `captchaCode` 字段
- **当前文档**: LoginRequest 类图中无此字段
- **建议修改**: 在类图中新增 `+String captchaCode` 行
- **标记位置**: docs/modules/auth.md 技术设计 > 类图

### 需人工处理 (👤)
- ⏭️ (无)

### 统计
- 已修复: 3 项
- 已标记待确认: 2 项
- 需人工: 0 项
```

### 5.4 L3: 高度同步

**目标**：L2 全部功能 + 语义检查 + 更详细建议 + 上下文分析

**执行流程**：

```
1. 运行 L0 漂移检测
2. 修复所有 🤖 漂移项
3. 对每个 🤖👤 漂移项（增强处理）：
   a. 读取完整的 Service/Facade 源码（不只是变更方法）
   b. 理解整体业务上下文
   c. 检查关联文档章节是否也需要同步
   d. 生成更精确的修改建议，包含修改理由
   e. 在文档中标记 [待确认] 并附带修改理由
4. 语义检查：
   a. 检查文档内部一致性（API 参考与技术设计是否矛盾）
   b. 检查跨文档引用是否有效（AGENTS.md 索引表中的路径是否指向存在的文档）
   c. 检查 OpenSpec Intent 轨是否需要更新
5. 对每个 👤 漂移项（增强报告）：
   a. 提供参考模板或类似文档作为编写依据
   b. 建议优先级
   c. 估算工作量（小/中/大）
6. 输出增强版同步报告
```

**同步报告格式（L3 扩展）**：

```markdown
## L3 同步报告

### 已修复 (🤖)
- ✅ ...（同 L2）

### 已标记待确认 (🤖👤) — 含详细分析

#### docs/modules/auth.md — 技术设计: AuthService.login 描述更新
- **代码事实**: AuthService.login() 新增 `captchaCode` 参数校验，校验逻辑委托给 CaptchaService
- **当前文档**: "用户登录时校验用户名和密码"
- **建议修改**: "用户登录时校验用户名、密码和验证码。验证码通过 CaptchaService 验证。"
- **修改理由**: captchaCode 校验是新增的防御性逻辑，应在描述中体现
- **关联检查**:
  - API 参考: LoginController.login 的参数列表已同步 ✅
  - 类图: LoginRequest.captchaCode 已标记 [待确认] ✅
  - 时序图: 未包含验证码校验步骤 → 建议新增 [待确认]
- **标记位置**: docs/modules/auth.md 技术设计章节

### 语义检查结果

#### 文档内部一致性
- ⚠️ docs/modules/auth.md: API 参考中列出 5 个端点，技术设计中只描述了 3 个方法
  → 可能缺少 Facade 层方法的描述

#### 跨文档引用
- ✅ AGENTS.md 索引表中的所有路径均有效
- ⚠️ docs/modules/client-cache.md 引用了 docs/conventions/configuration.md 中的 "配置参考" 章节，但该章节标题为 "配置项参考"（标题不匹配）

#### OpenSpec Intent 轨
- ⚠️ openspec/specs/auth/spec.md 中的登录流程描述可能需要更新以反映验证码校验

### 需人工处理 (👤) — 含操作建议

#### 新模块文档创建: clients/client-payment
- **建议动作**: 创建 docs/modules/client-payment.md
- **参考模板**: docs/modules/client-cache.md
- **优先级**: 高
- **工作量**: 中（约 30 分钟）
- **模板来源**: client-cache.md 的结构可直接复用，需替换具体配置项和 API

### 统计
- 已修复: 3 项
- 已标记待确认: 3 项（含 1 项关联检查发现）
- 语义问题: 3 项
- 需人工: 1 项
```

---

## 附录 A: md-sections 错误处理

| 错误码 | 含义 | 处理方式 |
|--------|------|---------|
| `missing_file` | 未指定文件路径 | 检查调用参数 |
| `file_not_found` | 文件不存在 | 检查文档路径是否正确，可能是新模块尚未创建文档 |
| `not_found` | 标题未匹配 | 检查 JSON 章节树中的实际标题文本，可能标题措辞不同 |
| `line_not_found` | 行号不在任何章节内 | 行号超出文件范围 |

**`not_found` 错误的恢复流程**：

```bash
# 假设 "API 参考" 未命中，脚本返回：
# {"error":"not_found","missing":["API 参考"],"children":[...]}

# 1. 检查返回的 children 子树，找到实际标题
# 可能实际标题是 "API 端点" 或 "接口参考"

# 2. 用实际标题重试
scripts/md-sections.sh docs/modules/{module}.md "API 端点"
```

## 附录 B: Edit 工具使用注意事项

1. **oldString 必须精确匹配**：从 md-sections 输出中复制原文，保留所有空格和换行
2. **避免歧义匹配**：如果 oldString 可能在文件中出现多次，扩大上下文范围使其唯一
3. **保持缩进**：表格行、列表项的缩进必须与原文一致
4. **表格行操作**：
   - 新增行：找到表格最后一行，在其后追加
   - 删除行：将目标行替换为空字符串，注意处理前后的 `\n`
   - 修改行：精确匹配整行内容
5. **Mermaid 图操作**：
   - 代码块围栏（```）不能作为 oldString 的一部分
   - 只修改围栏内部的图表内容

## 附录 C: 同步级别选择指南

| 场景 | 推荐级别 | 理由 |
|------|---------|------|
| CI/CD 自动检查 | L0 | 只检测不修改，适合自动化流水线 |
| 提交前快速检查 | L1 | 快速修复明显的版本号/端点不一致 |
| 功能开发完成后的文档同步 | L2 | 平衡自动化效率和人工确认需求 |
| 版本发布前的全面文档审计 | L3 | 最全面，包含语义检查和关联分析 |
| 重构后的文档对齐 | L3 | 重构影响面广，需要深度检查 |


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)