首页
/ impeccable doctor:报告并修复 Impeccable 项目工件漂移的维护指南

impeccable doctor:报告并修复 Impeccable 项目工件漂移的维护指南

2026-09-07 15:06:19作者:彭桢灵Jeremy

doctor 是 impeccable 设计语言体系中的一项维护型命令:它系统性检查项目内 Impeccable 工件(PRODUCT.md、DESIGN.md 及其 .impeccable/design.json 侧车文件、.impeccable/config.json、持久化的 surface brief、以及 design hook)与当前所安装版本所读取的结构之间是否发生漂移,并输出可执行、可分级、可自动修复的报告。本文基于 skill/reference/doctor.md(即本文档的规范源,另在多份 harness 目录如 .grok/skills/impeccable/reference/doctor.md.claude/skills/impeccable/reference/doctor.md 中同步分发)整理,并结合仓库内的 CLAUDE.mdskill/SKILL.src.md 与真实配置文件展开。读完你将掌握如何运行一次 doctor pass、如何按严重级别采取行动、如何处理单仓与 monorepo 下的典型 finding,以及如何关停会话启动时的廉价检查。

一、doctor 拥有什么,不拥有什么

doctor 的第一条纪律写在文档开头:这是维护,不是设计。不得重新设计任何内容,不得打开报告点名范围之外的文件,也不得作为副作用去运行任何其他命令。

它负责的范围是"项目 Impeccable 工件 vs 安装版本所读取的结构"之间的漂移,包括:

  • PRODUCT.md(产品记录,归 init 所有);
  • DESIGN.md 及其 .impeccable/design.json 侧车(设计记录,归 document 所有);
  • .impeccable/config.json(以及按开发者的本地覆盖文件);
  • 持久化的 surface brief(方向性契约);
  • design hook 配置。

.impeccable/config.json 中可以看到本仓库的真实配置形态,它同时管理检测器与 hook 两个区块:detector(含 ignoreRulesignoreFilesignoreValues)与 hookenabledlimits.maxFindings/maxChars),此外还有 init/document 等命令按需写入的键。

"out of date" 一词之下其实混着三种性质完全不同的漂移,必须分开对待:

漂移类型 本质 责任归属 说明
工具版本漂移(Tool version) 安装的 skill 比已发布版本旧 npx impeccable update context.mjs 在启动时以 UPDATE_AVAILABLE 上报,不是本命令的职责
模式漂移(Schema drift) 工件由更老的 Impeccable 写成:存在无人读取的字段、缺失现在期望的字段、文件位于已退役的位置 doctor 本身(机械修复) 大部分可由 doctor 修复
事实漂移(Truth drift) 代码已演进,文档不再描述它 document 拥有 DESIGN.md,init 拥有 PRODUCT.md 没有文件比对能裁决它;doctor 的职责是把"具体缺口"而非"模糊怀疑"交给它们

换句话说,doctor 只在"结构一致性"上有裁决权,在"内容真伪"上它只负责转交一个定位精确的任务单。

二、第 1 步:运行一次 pass

运行 doctor 的标准命令在本文档源中以模板路径写作:

node {{scripts_path}}/doctor.mjs --json

在某个具体 harness 安装中,例如:

node .grok/skills/impeccable/scripts/doctor.mjs --json

对应的技能路由入口为 /impeccable doctor(见 skill/SKILL.src.md 第 83 行的 Doctor 规则块,插件分发版见 plugin/skills/impeccable/SKILL.md)。需要说明的是:doctor.mjs 本体属于随安装分发的脚本,仓库快照的 skill/scripts/ 目录中并未驻留该脚本(CLAUDE.md 中将其描述为按需运行的 Tier 2 组件),运行前请确认你的安装已就位。

指定目标: 当用户在 monorepo 中点名了一个 workspace、文件或路由时,追加:

node {{scripts_path}}/doctor.mjs --json --target <path>

不带 --target 时,报告描述的是 repo 根目录;而在 monorepo 中这常常是错误的那个项目——这是文档明确提醒的第一处易错点。

输出结构: 报告携带两类载荷:

  • findings:每个 finding 均含 idartifactpathseveritysummaryfix 六个字段;
  • workspaces(monorepo 时):包含每个 app 的 product 与 design 解析结果。

此外还有旗标 ruleRegistryAvailable: false:当它为 false,说明被忽略的 rule id 无法校验(即 ignore 列表里的规则号在现有规则注册表中查不到)——此时要如实说明,而不是暗示这份忽略清单是干净的。

空结果即好消息。 findings 为空数组就是理想结局:用一行话说明并停止,不要画蛇添足。

三、第 2 步:按 severity 行动

severity 字段表达的是"应该发生什么",而不是"问题有多糟"。它对应三档行动:

severity 含义 你的动作
auto 不携带任何决策 运行一次 node {{scripts_path}}/doctor.mjs --fix 统一应用这些修复,然后用一行汇报它移动了什么。先征求许可、事后追问都不需要
mention 用户需要知情,但现在无需决策 用一句话陈述每一条及其给出的修复建议
route 需要一条具体命令 说出那条命令及它将关闭的缺口;仅当用户在本轮明确要求时才运行——initdocument 是对话式流程,不是你可以无人值守执行的修复

三条纪律值得重申:三组 severity 必须在一次汇报中全部覆盖;findings 不是错误,命令不会因为它们而失败;--fix 只应用 auto 类 finding,且只在无需任何判断的地方动手(CLAUDE.md 第 50–71 行的 Artifact staleness 小节对此有完全一致的定义)。

配套的行为红线来自 plugin/skills/impeccable/SKILL.md 第 85 行:绝不在设计任务中把修复漂移当副作用顺手做了CONTEXT_STALE finding 应被"报告"而非"代为行动",除非用户明确要求;唯一例外是标记为 auto 的 finding,它会在下一次对该文件的写入时被自动应用。

四、第 3 步:被弃用的字段是"绑定"的

当一个 finding 报告某字段已被弃用(当前实例是 ## Register 小节)时,它不是一条风格建议

从此刻起,无论该字段持有何值,都要在每一项决策中把它当作"不存在"来对待,并向用户提议删除该小节。文档给出的理由一针见血:"just in case 地保留它,正是一个退役的轴继续操纵当前输出的方式。" 残留的旧字段会持续向后续会话的输出施加方向性影响——这正是 schema 漂移中最隐蔽的一种。保留与否没有中间态:要么删除,要么它就一直"活着"。

五、第 4 步:不要在 truth drift 上过度断言

design-md-drift 这条 finding 统计的是:自 DESIGN.md 上次编辑以来,视觉源码目录产生了多少次提交。但请注意:提交次数不等于矛盾。

  • 报告这个数字即可;
  • 说明它度量的是什么;
  • 如果用户想确认文档是否真的错了,那么把 DESIGN.md 与当前的 tokens、组件逐条对照,从对照结果回答;
  • 绝不因为数字大就断言 DESIGN.md 已过期。

同样的克制适用于 workspace-context-inherited:继承是设计好的行为,不是缺陷。一条产品记录是否如实地描述了多个 app,这是该问用户的问题,而不是需要你去修的 bug。把继承自动标记为漂移,等于把设计契约误读成脏数据。

六、Monorepo 专项 finding 手册

doctor 在 monorepo 场景下有几条几乎必然出现的 finding,文档按类别给出了明确的处置建议:

workspace-platform-native-evidence——monorepo 中最重要的一条。 当一个 workspace 携带原生构建文件(iOS/Android),却继承了一条解析为 web 的根记录时,它会终生得到 web 方向的引导,永远不会加载对应的 ios.mdandroid.md。修复方式是在该 workspace 放一个子级 PRODUCT.md——因为一条被继承的记录不可能同时容纳两个平台。

config-project-roots-match-nothing 意味着每个 projectRoots glob 都未命中,于是 repo 根目录被静默当作当前激活项目。最常见原因是 workspace 目录被改名。处置:报告这些 pattern,并询问它们应指向哪些目录。

config-invalid-build-pathconfig-build-path-unset——两条 finding 都只关心一个键: .impeccable/config.json 中的 buildPath(或被 gitignore 的 .impeccable/config.local.json,后者对某位开发者而言优先级更高)。它只取值 compcode,决定新 surface 是从生成的 comp 开始构建,还是直接在代码中构建:

{ "buildPath": "comp" }

关于该键的几条底层事实需要格外小心:

  • "读不到值"不会回退到另一条路径。 一个本意是 code 的项目,如果键不可读,实际会一直以 comp-led 方式构建——必须报告确切的值,而不是猜。
  • config-invalid-build-path 只在已有值但值非法时触发;config-build-path-unset 只在项目做过方向性工作却从未记录偏好时触发。
  • config-build-path-unset 中的"询问提供"只有在你的工具面存在图像生成时才成立。没有图像生成能力,就既没有可选择的选项,也没有该说的话——因为 comp 优先的前提是能生成 comp。

buildPath 的记录时机与回退逻辑可以在 README.mdskill/reference/init.mdskill/reference/new-work.md 中交叉印证:init 在图像生成可用且未记录偏好时只问一次并写回;new-work 每次执行方向会话时从 buildPath 读取默认、以 { value, toggle } 形式下发页面页脚开关;本机与团队提交值不一致时 config.local.json 胜出;两者皆无且存在图像生成时,默认走 comp 优先。

在任何变更提议之前,先用 workspaces 表向用户展示: 哪些 app 自带上下文、哪些是继承、哪些什么都没有。先给全景,再谈改动。

七、底层实现:两级的 staleness 检查

CLAUDE.md 第 50–71 行可以还原 doctor 背后的完整实现设计,它分为两个层级:

Tier 1 —— 会话启动的廉价子集。 context.mjs 在启动时报告这些 findings 中"廉价"的那部分,以每项目每周一次的节流输出(lib/staleness-notice.mjs 负责节流,缓存落在 ~/.impeccable/staleness-check.json)。启动输出已经很重,因此 Tier 1 对整组 finding 只发一条 CONTEXT_STALE 指令。工具版本漂移则由 context.mjscomputeUpdateDirective() 计算,输出为 UPDATE_AVAILABLE

Tier 2 —— 按需的深度 pass。 由 doctor 本体执行,覆盖四个工作维度:Git log(服务于 design-md-drift 这类统计)、按 workspace 的逐个扫描、针对实时 ANTIPATTERNS 注册表的 ignore-list 校验(对应 ruleRegistryAvailable)、以及 hook 脚本解析。

Findings 是数据,不是噪声。 { id, artifact, path, severity, summary, fix } 这一统一形状贯穿三处消费方:启动指令、文本报告、--json 输出——同一个 finding 集在不同通道中以同一语义渲染。节流上有一条巧妙的不对称:auto finding 永不节流、也永不向用户展示(它们只等下一次写入静默生效),而 mentionroute 则被节流。

八、退出启动时的自动检查

如果你只想在主动询问时才看到报告,有两种退出方式:

  • 持久关闭:.impeccable/config.json 中写入 "stalenessCheck": false
  • 单会话关闭: 设置环境变量 IMPECCABLE_NO_STALENESS_CHECK=1

要点:关闭检查后 doctor 命令本身依然可用。这正是向"只想被问时才看报告"的用户推荐组合——启动时安静,随时能手动深查。(顺带一提,CLAUDE.md 提醒:任何断言其他启动指令的测试都应设置该环境变量,这也是其更新检查测试套件的做法。)

九、doctor 在命令体系中的位置:工具而非设计命令

doctor 刻意被排除在"设计菜单"之外。根据 skill/SKILL.src.md 的规则块,doctor 遵循 hookspin 一类的模式——SKILL.src.md 中一行 + reference/doctor.md 一份参考文档,而不是 Commands 表模式。它不在 IMPECCABLE_SUB_COMMANDScommand-metadata.jsonSKILL_CATEGORIES,也不在 pin.mjsVALID_COMMANDS 中,因此不计入那 23 个设计命令

理解这条边界对正确使用至关重要:doctor 是维护工具,它的价值在于"让写文档的人拿到具体缺口",而不是取代设计会话。与之配套,skill/SKILL.src.mdplugin/skills/impeccable/SKILL.md 都注明:启动输出中的 CONTEXT_STALE 是指向同一份报告的廉价子集,应按它自身的指令就地处理,而不是擅自代跑 doctor。当用户问起"什么过期了、什么过时了、什么需要刷新"时,doctor 才是那个要加载的参考。

十、一份可复制的操作清单

把整份指南压成一次 pass 的操作序列:

  1. 运行 node {{scripts_path}}/doctor.mjs --json;monorepo 或用户点名目标时追加 --target <path>
  2. 逐项读 findingsid/artifact/path/severity/summary/fix)与 workspaces 表,留意 ruleRegistryAvailable: false
  3. auto 运行一次 --fix 并一行汇报;对 mention 一句话陈述;对 route 点名命令、等待用户确认后再跑(initdocument 是对话,不无人值守执行);
  4. 将弃用字段(如 ## Register)视为不存在,提议删除;
  5. design-md-drift 只报数字与其度量含义,绝不凭提交数断言文档过期;
  6. monorepo 中优先处理 workspace-platform-native-evidence,核对 projectRoots glob 与 buildPath 的真实取值,改任何东西之前先亮出 workspaces 全景;
  7. findings 数组即收工;用户只想要按需报告时,关闭 stalenessCheck 或设置环境变量即可,doctor 随时可再用。

这样的纪律保证了:doctor 永远把"结构问题"修得干净利落,把"内容问题"交还给该写文档的命令,而不让一次维护动作悄悄演变成一场未授权的设计改动。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.81 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
596
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
920
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.79 K
1.02 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
519
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
390