关于 blueprint-adr-validate
面向维护 Architecture Decision Records(ADR)的项目团队,用于检查决策记录的引用、关系和领域冲突,并支持交互式修复或仅生成报告。
主要功能
- 01检查 ADR 引用与关系完整性
- 02识别循环引用和自引用
- 03检测编号冲突及索引偏差
- 04分析领域冲突与未标记记录
- 05交互式修复或仅生成报告
适用场景
- 01发布前检查 ADR 一致性
- 02重构后审计决策记录
- 03持续维护 ADR 目录和索引
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ laurigates/ claude-plugins --skill blueprint-adr-validate安装后在 Claude Code 中输入/blueprint-adr-validate确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
REFERENCE.mdscripts/check-adr-numbers.shgenerate-adr-index.shscripts/tests/test-check-adr-numbers.shtest-generate-adr-index.sh详细介绍
/blueprint:adr-validate
验证架构决策记录(ADR)的关系一致性、引用完整性和领域冲突。
用法:/blueprint:adr-validate [--report-only]
何时使用此技能
| 在以下情况下使用此技能…… | 在以下情况下使用替代方案…… |
|---|---|
| 在发布前维护 ADR 的完整性 | 创建新的 ADR(使用 /blueprint:derive-plans) |
| 重构或更改后进行审计 | 快速进行一次性文档审查 |
| 定期进行文档审查 | 通用 ADR 阅读 |
上下文
- ADR 目录是否存在:!
find . -path '*/docs/adrs' -maxdepth 2 -type d - ADR 数量:!
find . -path '*/docs/adrs/*' -name "*.md" -type f - 带领域标签的 ADR:!
find . -path '*/docs/adrs/*' -maxdepth 3 -name '*.md' -exec grep -l "^domain:" {} + - 标志:!
echo "${1:---}"
参数
解析 $ARGUMENTS:
--report-only:输出验证报告,不提示修复- 默认:交互模式,提供修复选项
执行
执行完整的 ADR 验证与修复工作流:
步骤 1:发现所有 ADR
- 检查
docs/adrs/中是否存在 ADR 目录 - 如果不存在 → 错误:"No ADRs found in docs/adrs/"
- 解析所有 ADR 文件:
ls docs/adrs/*.md - 提取每个 ADR 的 frontmatter:number、id、created、modified、status、domain、supersedes、superseded-by、extends、relates-to
步骤 2:验证引用完整性
对每个 ADR,验证:
- supersedes 引用:验证目标存在、目标状态为 "Superseded",且目标具有对应的 superseded-by
- extends 引用:验证目标存在;如果目标为 "Superseded",则发出警告
- 相关引用:验证所有目标均存在;如果链接为单向,则发出警告
- 自引用:如果 ADR 引用了自身,则标记
- 循环链:检测取代关系图中的循环
- 跨工作区引用(v3.3.0+,清单中包含
workspaces.role): 识别 supersedes/extends/related 字段中的以下引用形式:ADR-NNN— 当前工作区内的引用(现有行为)。<workspace-path>/ADR-NNN— 指向同级或子工作区。通过 monorepo 根目录下的<workspace-path>/docs/adrs/解析。 如果该工作区未列在根目录的workspaces.children中,则发出警告。/ADR-NNN— 指向 monorepo 根目录的 ADR 集合。对于子清单,使用清单中的workspaces.root_relative_path解析;对于根清单,使用当前目录解析。 未解析的跨工作区引用会作为警告(而非错误)报告,因此迁移期间不会阻止验证。
详细检查项请参阅 REFERENCE.md。
步骤 2b:检测 ADR 编号冲突和索引漂移
ADR 编号在分支时选定,但直到合并时才正式占用,因此两个正在进行的 ADR PR 可能选中相同编号,最终都合并成功(问题 #1585——FVH 基础设施 #2015 冲突,两个 ADR 都声明了 0038)。运行确定性检查:
bash ${CLAUDE_SKILL_DIR}/scripts/check-adr-numbers.sh --project-dir "$(pwd)"
它会输出结构化的 STATUS= / ISSUE_COUNT= 格式,并报告以下四类问题(参见 REFERENCE.md):
duplicate_adr_number(ERROR)——两个文件声明了相同编号,可通过NNNN-title.md文件名或 frontmatter 中的id: ADR-NNNN声明判断。消息会注明来源(basename/frontmatter),因此,即使文件名中没有编号,其声明也一目了然。adr_number_collision(ERROR)——工作树中的 ADR 声明的编号已由基准引用(origin/main)上的另一个文件占用——这是合并前并行 PR 的情况,可在第二个 PR 合并前发现。adr_registry_mismatch(ERROR)——文件声明了ADR-NNNN,但清单中的id_registry将该编号映射到另一个路径。注册表是唯一能明确判定 id → path 的依据:两个文件都可以声称是ADR-0016,但只能有一个文件注册。没有清单 / 没有id_registry时,此项不报告。adr_missing_index_row(WARN)——ADR 未列入目录的 README 索引(这正是0038冲突未被发现一周的原因)。请通过步骤 2c 修复,而不是再次发出警告。
多个 ADR 目录(问题 #2129):检查程序会扫描一组目录——实际的 ADR-0016 冲突中,声明者分别位于 docs/adrs/ 和 docs/blueprint/adrs/,单目录扫描无法发现这种情况。优先级如下:可重复传入的 --adr-dir <dir>;否则使用共享的 validation 清单区块中的 validation.adr_dirs(问题 #2128,通过 blueprint-plugin/scripts/get-validation-config.sh 读取——不要添加第二种配置机制);否则按 docs/adrs、docs/adr 的顺序使用默认值。输出中会包含 ADR_DIRS=(以 TAB 分隔)和 ADR_DIR_COUNT=;ADR_DIR=、ADR_COUNT= 和 INDEX= 保留其在 #1585 中的含义。
将检查结果纳入步骤 4 的报告。STATUS=ERROR 表示存在阻塞性冲突:将较新的 ADR 重新编号为下一个可用编号(同时更新其文件名和 frontmatter 中的 id:),然后重新生成索引(步骤 2c)。
步骤 2c:重新生成 ADR 索引
adr_missing_index_row 可以修复——问题出在手动维护的表格已经过时。根据 frontmatter 重新生成索引:
# CI / read-only gate: non-zero exit when the on-disk index is out of date
bash ${CLAUDE_SKILL_DIR}/scripts/generate-adr-index.sh --project-dir "$(pwd)" --check
# Repair: rewrite the table in every resolved ADR directory
bash ${CLAUDE_SKILL_DIR}/scripts/generate-adr-index.sh --project-dir "$(pwd)"
只会重写两个标记注释之间的区块,因此周围的说明文字会保留:
| ADR | Title | Status |
| --- | ----- | ------ |
| [ADR-0016](0016-in-engine-ui.md) | In-engine UI | Accepted |
每个目录各有一个索引(--index-file 可覆盖默认的 README.md),使用与步骤 2b 相同的目录集合和扩展后的编号收集范围——因此,即使 ADR 编号只出现在 frontmatter 中,也会生成对应行。--check 会报告每个目录的状态:
| 状态 | 严重级别 | 含义 |
|---|---|---|
in_sync | OK | 磁盘上的区块与生成内容一致 |
drifted | ERROR(退出码 1) | 需要重新生成——索引已过时 |
markers_absent | WARN(退出码 0) | 尚无标记对;写入时会添加标记。这有意不作为差异处理 |
index_absent | WARN(退出码 0) | 索引文件尚不存在;写入时会创建 |
no_adrs | OK | 目录中没有带编号的 ADR |
写入模式会报告 written / unchanged / markers_inserted / index_created。如果标记插入到已有手写表格的 README 中,请删除该表格——生成的区块才是权威版本。
步骤 3:分析领域
- 按领域字段对 ADR 分组
- 对每个包含多个“Accepted”ADR 的领域 → 标记为潜在冲突
- 列出未标记领域的 ADR(不是错误,而是建议)
步骤 4:生成验证报告
汇总生成完整报告,包含:
- 摘要:ADR 总数、标记领域的比例、关系数量、状态细分
- 引用完整性:Supersedes、extends、related 状态(✅/⚠️/❌)
- 发现的错误:无效引用、自引用、循环
- 警告:过时的扩展、单向链接
- 领域分析:冲突和未标记领域的 ADR
步骤 5:处理 --report-only 标志
如果存在 --report-only 标志:
- 输出步骤 4 中的验证报告
- 退出,不提示用户修复
步骤 6:提示进行修复(交互模式下)
通过 AskUserQuestion 询问用户要采取的操作:
- 自动全部修复(更新状态,添加相互链接)
- 逐项检查问题
- 将报告导出到
docs/adrs/validation-report.md - 暂时跳过
根据所选操作执行(参见 REFERENCE.md)。
步骤 7:更新任务注册表
更新 docs/blueprint/manifest.json 中的任务注册表条目:
jq --arg now "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
--arg result "${VALIDATION_RESULT:-success}" \
--argjson processed "${ADRS_VALIDATED:-0}" \
'.task_registry["adr-validate"].last_completed_at = $now |
.task_registry["adr-validate"].last_result = $result |
.task_registry["adr-validate"].stats.runs_total = ((.task_registry["adr-validate"].stats.runs_total // 0) + 1) |
.task_registry["adr-validate"].stats.items_processed = $processed' \
docs/blueprint/manifest.json > tmp.json && mv tmp.json docs/blueprint/manifest.json
其中 VALIDATION_RESULT 为 "success"、"{N} warnings" 或 "failed: {reason}"。
步骤 8:报告变更和摘要
报告所做的所有变更:
- 已更新的 ADR(状态变更、添加的链接)
- 剩余问题数量
- 后续步骤建议
Agentic 优化
| 情境 | 命令 |
|---|---|
| 检查 ADR 目录 | test -d docs/adrs && echo "YES" || echo "NO" |
| 统计 ADR 数量 | ls docs/adrs/*.md 2>/dev/null | wc -l |
| 提取 frontmatter | head -50 {file} | grep -m1 "^field:" | sed 's/^[^:]*:[[:space:]]*//' |
| 按领域查找 | grep -l "^domain: {domain}" docs/adrs/*.md |
| 检测循环 | 构建取代关系图并遍历 |
| 编号冲突 | bash ${CLAUDE_SKILL_DIR}/scripts/check-adr-numbers.sh --project-dir "$(pwd)" |
| 两个 ADR 目录 | … /check-adr-numbers.sh --adr-dir docs/adrs --adr-dir docs/blueprint/adrs |
| 索引漂移门禁(CI) | bash ${CLAUDE_SKILL_DIR}/scripts/generate-adr-index.sh --check |
| 重新生成索引 | bash ${CLAUDE_SKILL_DIR}/scripts/generate-adr-index.sh |
有关验证规则、修复流程和报告格式的详细信息,请参见 REFERENCE.md。