Impeccable doctor 命令实战指南:检测与修复项目工件漂移
Impeccable 是一套让 AI 设计工作流(Agent/harness)更懂设计的技能与引擎,它以 PRODUCT.md、DESIGN.md 及其 .impeccable/design.json 伴生文件、.impeccable/config.json、持久化 surface briefs 和 design hook 等一批"工件"(artifact)作为设计决策的权威来源。本文讲解 impeccable doctor 这一维护型命令:如何在项目工件与已安装的 Impeccable 版本所读取的格式之间检测"漂移"(drift),如何按严重性分级处置,以及 monorepo 场景下的专项检查。读完你将掌握 doctor 的完整操作流程、每个 finding 的语义与修复动作,并理解其背后的源码实现逻辑。
doctor 的职责边界:维护,而非设计
doctor 是维护性命令,不是设计命令。它的职责是:报告并修复本项目的 Impeccable 工件与当前安装版本所读取内容之间的漂移,检查对象包括:
PRODUCT.md(产品真相记录)DESIGN.md及其.impeccable/design.json伴生文件(设计系统记录).impeccable/config.json(项目级配置)- 持久化的 surface briefs(
.impeccable/surfaces/下的简报) - design hook(接入各 harness 的自动检测钩子)
运行时不要重新设计任何东西、不要打开报告之外的文件、不要作为副作用执行其他命令。源码 doctor.rs 中的用法说明也印证了这一点:Report drift between this project's Impeccable artifacts and what the installed version reads。
三种漂移:先分清楚再动手
"过时(out of date)"名下其实混着三种完全不同的漂移,处理方式各异,必须分开对待:
| 漂移类型 | 含义 | 谁负责 | 是否本命令职责 |
|---|---|---|---|
| 工具版本漂移(Tool version) | 已安装的 skill 比发布版旧 | impeccable context 在启动时以 UPDATE_AVAILABLE 报告,npx impeccable update 修复 |
否 |
| Schema 漂移(Schema drift) | 工件由旧版 Impeccable 写出:含有无人读取的字段、缺少新版期望的字段、文件位于退役位置 | 机械性问题,doctor 可修复其中大部分 |
是 |
| 真值漂移(Truth drift) | 代码演进后文档不再描述现实 | 没有文件对比能裁决;document 拥有 DESIGN.md,init 拥有 PRODUCT.md,doctor 的职责是把具体缺口交给它们,而不是给出模糊怀疑 |
否(仅移交) |
这一分类在实现中同样可见:collect_boot_finding_groups(staleness.rs)负责 boot 期的机械性工件检查,而 check_design_drift 等深层检查(staleness_deep.rs)只负责给出"值得重读"的信号,不自行改写文档。
第一步:运行检查
在项目根目录(或 skill 安装目录)执行:
"<skill-base-dir>/scripts/impeccable" doctor --json
其中 <skill-base-dir> 是当前 harness 下 Impeccable skill 的安装目录(例如 .claude/skills/impeccable),scripts/impeccable 是平台启动器(launcher)。启动器源码见 skill/scripts/impeccable:它会依次尝试 $IMPECCABLE_BIN、随附平台二进制、~/.impeccable/bin/impeccable、版本固定缓存,最后才回退到 PATH 上的 impeccable,全程不需要 Node。
参数说明
doctor 的完整参数(与 doctor.rs 中的 parse_args 与 usage 文本一致):
| 参数 | 作用 |
|---|---|
--json |
以 JSON 形式输出 findings |
--fix |
只应用 severity 为 auto 的机械迁移 |
--target <path> |
在 monorepo 中选择某个 workspace / 文件 / 路由 |
--help / -h |
输出用法并退出,不运行任何检查(优先级最高) |
当用户点名了 monorepo 中的某个 workspace、文件或路由时,必须追加 --target <path>;不追加时报告描述的是仓库根,在 monorepo 中这往往是错误的项目。参数解析要求 --target 必须有值(--target <path> 与 --target=<path> 两种写法都支持,见 target_args.rs),--target 缺值会向 stderr 输出错误并以退出码 1 结束。
输出结构
--json 输出的顶层字段包括:projectRoot、repoRoot、isMonorepo、productPath、designPath、platform、ruleRegistryAvailable、findings、workspaces,以及(仅在使用 --fix 时)fixes。其中:
- 每个
finding携带id、artifact、path、severity、summary、fix六个字段(字段定义见 staleness.rs 的Finding结构); - monorepo 场景下
workspaces数组给出每个 app 的 product/design 解析结果(含productStatus/designStatus与platform,见 staleness_deep.rs 的WorkspaceRow); ruleRegistryAvailable: false表示无法加载内置检测器注册表,此时被忽略的规则 id 无法被校验,报告时必须如实说明,而不是暗示清单是干净的。
文本输出则按严重性分组展示(route/mention/auto),格式可参见测试金标准 tests/oracle/golden/doctor-legacy-fix.json:每个 finding 一行 id [path],接着 summary 与 → fix。
空 findings 数组是理想结果(文本输出为 No drift found. Every artifact matches what this version reads.,见 tests/oracle/golden/doctor-full-text.json),此时用一行说明"无漂移"并停止即可。findings 不是错误,命令不会因它们而失败(run 在正常情况下总是返回 0)。
第二步:按严重性分级行动
severity 字段描述的是"应该发生什么",而不是"问题有多严重":
auto(自动迁移):不携带任何决策。直接运行一次"<skill-base-dir>/scripts/impeccable" doctor --fix应用这些迁移,然后用一行汇报移动了什么。无需事先征求许可,事后也不必再问。mention(值得一说):用户需要知道,但此刻不需要做决定。用一句话陈述每条及其建议的修复。route(需要路由到专门命令):需要一个特定命令来修复。说出命令名及它能消除的缺口,仅当用户在本轮明确要求时才执行——init和document是访谈式对话流程,不是可无人值守执行的修复。
三组必须一次性全部报告,不要挑着说。
--fix 实际做什么
--fix 只处理 auto 严重性的项(doctor.rs 的 apply_fixes):
design-sidecar-legacy-path:当规范的.impeccable/design.json不存在时,把旧位置的侧车文件rename过去(输出Moved <旧路径> to <新路径>.);若规范位置已存在则跳过并注明already exists; not overwriting;legacy-live-state:跳过,提示"确认没有 live 会话运行后手动删除"(避免在会话活动时删除状态);- 其他
auto项若没有实现迁移,则记为no automatic migration implemented; - 非
auto项统一跳过,理由为needs a decision from the user。
此外,--fix 有一个与 findings 无关的独立动作:若 PRODUCT.md 存在、没有 schema 戳、且本轮没有 product-schema-legacy finding,则通过 stampProductSchema 写入 <!-- impeccable:product-schema 1 --> 戳(插入在 H1 之后),输出 Stamped PRODUCT.md as product-schema 1.。这正是 tests/oracle/golden/doctor-fix-stamps-product.json 验证的行为——第一次 --fix 打戳,第二次输出 Applied nothing.。Schema 版本常量定义在 artifact_schema.rs:PRODUCT_SCHEMA_VERSION = 1、DESIGN_SIDECAR_SCHEMA_VERSION = 2。
第三步:弃用字段具有约束力
任何报告了弃用字段的 finding(当前是 ## Register)都不是风格建议。从这一刻起,无论该字段持有何值,都必须把它当作不存在来对待,并主动提议删除该章节。
原因很现实:v4 用四种访客模式(Persuade / Operate / Read / Experience,按 surface 选择并持久化在该 surface 的 brief 中)取代了品牌/产品 register 轴,没有任何代码再读取 ## Register。实现上,artifact_schema.rs 的 PRODUCT_DEPRECATED_SECTIONS 记录了该章节及其废弃理由,而 staleness.rs 的 check_product 会为每个仍存在的废弃章节生成 product-deprecated-register finding(severity 为 mention)。"留着以防万一"恰恰是让退役的轴继续暗中影响当前输出的方式。
第四步:不要在真值漂移上过度断言
design-md-drift 统计的是自 DESIGN.md 上次编辑以来,视觉源目录上的提交数(源码见 staleness_deep.rs 的 check_design_drift)。视觉源目录固定为 src、app、pages、components、site、styles、public 七个(VISUAL_SOURCE_DIRS),检查仅在两件事同时成立时触发:项目在 git 仓库内,且自上次编辑 DESIGN.md 的提交 HEAD..last 之间对上述目录的提交数 ≥ 阈值 25(threshold 参数)。提交数不是矛盾证据:要报告这个数字及其衡量对象,如果用户想知道文档是否真的错了,就把 DESIGN.md 与当前 tokens 和 components 逐一对照,基于对照结果回答。绝不要因为数字大就断言 DESIGN.md 过时了。
同样的克制适用于 workspace-context-inherited:继承是设计好的行为(一个 workspace 继承仓库根的 PRODUCT.md 是刻意支持的),一条产品记录是否如实描述多个 app,是用户要回答的问题,不是需要修复的缺陷。
Monorepo 专项检查
monorepo 中 doctor 的 workspace 检查由 check_workspaces(staleness_deep.rs)驱动,要点如下:
workspace-platform-native-evidence是最关键的 finding:某个 workspace 携带原生构建文件,却继承了解析为web的根记录,那么它整个生命周期都只会得到 web 指导,永远不会加载 ios.md 或 android.md。修复方式是在该 workspace 内写一份子级 PRODUCT.md——因为一条继承记录无法同时承载两个平台。原生证据的识别(staleness.rs 的check_native_platform_evidence)检查两类信号:原生构建文件(pubspec.yaml、ios/Podfile、android/build.gradle、android/build.gradle.kts、ios/Runner.xcodeproj)以及 package.json 中的原生依赖(react-native、expo、@react-native/metro-config),并在有原生证据时建议## Platform取值(ios/android/ 多平台或混合时adaptive)。config-project-roots-match-nothing:表示projectRoots的每个 glob 都没有命中,仓库根被静默当作活动项目。重命名的 workspace 目录是常见诱因。报告这些模式并询问它们应该指向哪些目录。实现见check_project_roots:只要存在一个非!开头的正模式且候选数为 0,就触发该 finding。config-invalid-build-path与config-build-path-unset都围绕同一个键:.impeccable/config.json中的buildPath(或其 gitignore 的.impeccable/config.local.json,后者对特定开发者生效)。它只接受comp或code两个值(BUILD_PATH_VALUES),决定新 surface 是从生成的 comp 构建,还是直接在代码中构建:config-invalid-build-path:值无效,无效值不会回退到另一条路径,而是回退到默认值,因此一个本意是code的项目可能一直在走 comp 主导的构建流程——必须报告确切值;config-build-path-unset:仅在项目做过方向性工作(存在.impeccable/surfaces或.impeccable/mocks/decision,见DIRECTION_WORK_PATHS)却从未记录偏好时触发;并且只有你的工具面存在图像生成能力时才应给出选择提议(comp-first 或 code-first 二选一写入"buildPath": "comp"/"buildPath": "code",合并保留已有键)。没有图像生成能力就没有可选择的余地,保持沉默。
- 在提出任何改动前,先用
workspaces表向用户展示:哪些 app 携带自己的上下文、哪些继承、哪些完全没有(金标准输出见 tests/oracle/golden/doctor-monorepo-text.json:apps/a product: child design: child与apps/b product: inherited design: inherited)。
从源码看 doctor 的检查编排
doctor 的检查顺序固定(与 docs/CLI-CONTRACT.md 记载一致,见 doctor.rs 的 collect):check_product → check_native_platform_evidence(仅在存在 PRODUCT.md 时)→ check_design_sidecar → check_design_drift → check_design_coverage → check_config → check_build_path_unset → check_detector_ignores → check_surface_briefs → check_hook_installation → check_legacy_live_state → check_project_roots → workspace findings。其中 boot 期基础检查(Tier 1,staleness.rs)与 doctor 专属深层检查(Tier 2,staleness_deep.rs)交错执行,既复用 boot 策略又保持既有顺序。
值得注意的检查细节:
- 配置键白名单(
KNOWN_CONFIG_KEYS):hook、detector、updateCheck、stalenessCheck、projectRoots、buildPath、$schema、version。未知顶层键触发config-unknown-keys;detector对象的合法键为ignoreRules、ignoreFiles、ignoreValues、designSystem、extensions,常见的ignoreRule(少了 s)永远不会抑制任何东西(config-unknown-detector-keys)。 - 忽略规则校验:
check_detector_ignores会把ignoreRules中的 id 与内置注册表(staleness_deep.rs 的load_known_rule_ids,取自impeccable_core::registry::ANTIPATTERNS)比对,未命中的触发detector-ignore-rules-unknown;ignoreFiles中不存在的路径触发detector-ignore-files-missing。 - hook 健康检查:
check_hook_installation按 provider 查找清单(claude-code:.claude/settings.local.json、.claude/settings.json;codex/agents:.codex/hooks.json;cursor:.cursor/hooks.json;github:.github/hooks/impeccable.json;grok:.grok/hooks/impeccable.json,见 context_cli.rs 的hook_manifests_for),解析 hook 命令中的脚本路径(hook_markers.rs 的hook_program_token)并检查文件是否存在:缺失则触发hook-script-missing(hook 空转、UI 编辑一直未被扫描,但项目看起来"已覆盖"),修复方式是impeccable hooks on重装;清单已安装而配置设了hook.enabled: false则触发hook-enabled-conflict。 - surface brief 孤儿检测:
check_surface_briefs会找出primary_target指向已不存在文件、且不是 URL 或route:前缀的持久化 brief,触发surface-brief-orphaned——在该 brief 重定向或删除之前,它仍是已消失文件的"权威"。 - legacy live 状态:
.impeccable-live.json与.impeccable-live是退役位置(LEGACY_LIVE_PATHS),当前 live 模式写.impeccable/live/下。旧位置只通过向后兼容回退读取,无 live 会话运行时可安全删除。
选择退出启动时检查
impeccable context 在会话启动时会报告这些 finding 的"廉价子集"(collect_boot_findings,只做 Tier 1 检查,不做需要 git 的深层检查),且按项目每周最多一次节流。节流实现见 staleness_notice.rs:RENOTIFY_INTERVAL_MS 为 7 天,filter_fresh_findings 只放行距离上次通知超过 7 天的非 auto finding(auto 总是放行且不打时间戳),缓存默认落在 ~/.impeccable/staleness-check.json(可用 IMPECCABLE_STALENESS_CACHE 覆盖)。
退出方式有两种:
- 在
.impeccable/config.json中设置"stalenessCheck": false(永久关闭); - 设置环境变量
IMPECCABLE_NO_STALENESS_CHECK=1(仅本次会话)。
staleness_check_disabled 的判定是:环境变量非空,或任一 root 的 config.json / config.local.json 中 stalenessCheck === false(后者优先,后读覆盖先读)。关闭检查不影响 doctor 命令本身——对于只想在主动询问时才看到报告的用户,这就是推荐组合:关闭启动检查,需要时手动运行 doctor。
完整实操流程速查
- 确定项目根或 monorepo 目标:monorepo 中先
--target <path>; - 运行
doctor --json(或文本模式),一次性拿到全部 findings 与 workspaces 表; - 空 findings → 一行汇报"无漂移"并结束;
- 有 findings → 按
auto/mention/route三分组,全部汇报:auto直接跑doctor --fix,mention一句一句陈述,route点名命令与缺口、仅在用户要求时执行; - 任何
## Register等弃用字段按"不存在"处理并提议删除; design-md-drift等真值漂移只报告数字与含义,不臆断文档错误;- monorepo 中先把 workspaces 表摆给用户看,再谈任何改动。
doctor 的完整命令契约(参数、输入、输出格式、副作用、测试列表)可继续查阅 docs/CLI-CONTRACT.md,其行为由 oracle 金标准测试固化,如 tests/oracle/golden/doctor-legacy-fix.json、tests/oracle/golden/doctor-monorepo-roots-match-nothing.json 等。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0631
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
video-shotcraftAI宣传片skill,使用 Remotion 制作电影级产品视频:提供106 张镜头配方卡和可复用的视频魔板。适用于 Claude Code 与 Codex以及所有其他智能体Markdown00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python09
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00