☰
ZCode 架构治理故障排查实战指南:architecture:check 违规处理与基线维护
2026/10/1 20:35:04 网站建设 项目流程
  • 人工智能
  • 大模型
  • 代码智能体
  • AI Agent
  • 桌面应用
  • 后端
  • 前端
  • CLI

【免费下载链接】ZCode

ZCode 是 AI 编程工作台,提供桌面应用、浏览器界面和终端 Agent。本仓库包含客户端、后端服务、共享 UI,以及 Agent CLI 与运行时源码。

项目地址:https://gitcode.com/zai-org/ZCode
点击查看免费下载

ZCode 仓库内置了一套基于策略文件与静态检查的架构治理体系,用于在编码前约束模块边界、层依赖与公开契约。本文以.agents/skills/architecture-governance/references/troubleshooting.md为主体,结合 architecture-policy.yaml 与 scripts/architecture 下的可执行源码,系统梳理pnpm architecture:check常见违规的修复方案、变更范围与基线的正确用法,以及检查器当前已知的解析局限,帮助你准确解读报告、快速消除新的违规并维护既有的遗留基线。

一、先建立排查上下文:治理工作流速览

故障排查不是孤立步骤,它发生在完整的架构治理工作流中。按照 SKILL.md 的定义,治理以"预编码决策"为核心:

  1. 编码前用pnpm architecture:check --changed识别本次变更涉及的文件与模块;
  2. 用pnpm architecture:context <module-id>(或node .agents/skills/architecture-governance/scripts/context-package.mjs <module-id>)生成有界上下文阅读包;
  3. 写出或更新 spec,明确行为、所有权、不变量与迁移边界;
  4. 编码后再次运行pnpm architecture:check --changed,把"新增违规"与"基线违规"分开报告。

可执行策略只存在于根目录的 architecture-policy.yaml,SKILL.md与AGENTS.md均不重复规则内容。当检查报错时,你面对的大多是下面这五类规则级问题,或两类工作流级问题(变更范围、基线不一致)。先判断属于哪一类,再选择对应修复手段。

二、规则级故障速查:五种典型违规的修复方案

原文档给出了五类最常出现的违规及其修复方向,下面结合检查器源码逐一展开,说明"为什么会报"与"怎么修"。

1.module-dependency:跨模块导入未声明依赖

含义:一个模块的源码 import 了另一个模块的文件,但在策略的requires(或该模块module.ts的本地清单)中没有声明这个依赖。

修复:先确认这个跨模块边的"属主",然后要么引入公开契约,要么在本地 manifest 与策略中同时声明该依赖。检查器如何判定可以看 scripts/architecture/index.mjs:当 import 目标落在另一个模块时,会取manifestRequiresByModule(优先读module.ts中的requires,没有则回退到策略里的module.requires),若未包含目标模块 ID 即报module-dependency。因此"本地 manifest 与策略保持一致"不是建议而是判定前提——policy.mjs 的manifestRequires会从module.ts里正则提取requires数组,二者不一致时以 manifest 声明为准。

2.deep-import:导入绕过了模块公开入口

含义:跨模块导入没有走目标模块声明的公开入口(如contract.ts或index),而是直接点到了内部实现文件。

修复:改用目标模块声明的公开入口点。检查器在 index.mjs 中只有当forbidDeepImports: true且目标模块配置了publicEntrypoints时才触发该规则,通过 policy.mjs 的publicEntrypointMatches判定:目标文件必须精确等于策略中publicEntrypoints条目解析出的路径之一(条目可相对仓库根目录,也可相对模块 root)。也就是说,只要访问的是packages/services/src/storage/contract.ts这类公开入口,就不会报deep-import。

3.cycle:托管依赖图存在环

含义:模块间的依赖关系形成了环,比如 A 依赖 B、B 又依赖 A(或更长路径成环)。

修复:把共享类型下沉到契约模块,或者通过端口(port)反转依赖方向。检查器在 index.mjs 用 DFS 对文件级 import 边做环检测,并且会受managedOnly: true影响——只有当next节点属于 managed 模块时才继续遍历,因此环检测主要约束托管模块。报告里会以detail -> detail的形式列出环上的模块 ID 序列,方便你定位断环点。

4.missing-module-artifact:托管模块缺少必要产物

含义:标记为managed: true的模块缺少module.ts或contract.ts中的某一个(检查器对每个托管模块强制要求这两个文件,见 index.mjs)。

修复:补充报告所缺的产物。原文档明确说明是"manifest、contract、example 或 contract document"四类中的哪一类,就补哪一类。以 golden-module 夹具为最小合规样例:module.ts声明id、requires、provides与publicEntrypoints;contract.ts 只放窄端口;contract.example.ts 给出类型化使用示例;CONTRACT.md 记录类型无法表达的不变量。仓库中实际开启治理的托管模块是storage(见 architecture-policy.yaml),它声明了requires: [shared, rpc, services]、公开入口packages/services/src/storage/contract.ts以及domain/app/adapters三层结构,可作为真实规模下的参照。

5.expired-exception:配置的例外已过期

含义:策略exceptions中带有expires日期的例外项已过期。

修复:先解决底层的违规本身,然后移除过期例外,而不是静默延长它。检查器在 index.mjs 用new Date().toISOString().slice(0, 10)取当天日期,任何expires早于今天的例外都会产生全局违规。这是治理系统"例外必须有时限"的强制手段:临时豁免不是永久的逃生门,到期后必须用真实修复替换。当前仓库根部的 architecture-policy.yaml 中exceptions: [],.architecture-baseline.json 的违规列表也为空,说明基线处于干净状态。

更完整的规则全集见 rule-catalog.md,其中还包含max-file-lines、max-contract-lines、max-public-methods、layer-direction、domain-io、ui-implementation-import、disable-count等规则及其典型修复方向。

三、变更范围问题:--changed与全量扫描的区别

原文档明确提醒:pnpm architecture:check --changed选取的是相对HEAD的差异加上未跟踪文件。要理解这句表述,需要看两个实现细节。

首先,变更集合来自 git:index.mjs 的changedFilesFromGit并行执行git diff --name-only -z HEAD与git ls-files --others --exclude-standard -z,合并去重。也就是说:

  • 已提交(committed)的改动不再是 dirty worktree 的差异,--changed不会覆盖它们;
  • 未跟踪的新文件会被算入变更集,所以新建文件也会触发检查。

其次,--changed不是简单的"只看这些文件"。在 index.mjs 中,检查器会根据 import 边做反向依赖闭包:从变更文件出发,把所有依赖它们(即被它们 import 或间接依赖它们)的文件也纳入变更集,再过滤出global违规或落在闭包内的违规。这样做的意义是:你只改了 A 文件,但 A 的边界违规会让依赖 A 的 B 文件连锁暴露问题,--changed能把这些受影响文件一并纳入报告。

因此排查时应遵循原文档给出的两条准则:

  • 日常增量检查用pnpm architecture:check --changed;
  • 审查已提交改动、或需要完整视图时,运行不带--changed的pnpm architecture:check做全量扫描——全量扫描不受 dirty worktree 限制。

四、基线不一致问题:什么情况下才能更新基线

architecture:check的默认输出会区分"基线违规"与"新增违规":每个违规都会生成一个由规则名、文件路径与 detail 拼接后 SHA-256 取前 16 位的指纹(见 index.mjs),然后与 .architecture-baseline.json 中记录的指纹比对:命中指纹的归入baselineViolations,未命中的归入newViolations。最终退出码取决于新增违规是否为零(见 architecture-check.mjs)。

这意味着出现"基线不一致"时,正确姿势是原文档强调的:

  1. 先检查失败本身,确认新增违规是否真实存在、规则与文件是否匹配;
  2. 只有在经过明确评审的基线变更(即有意的遗留违规调整)时,才使用pnpm architecture:baseline:update;
  3. baseline:update会把当前全部违规(含基线与新违规)整体写回.architecture-baseline.json(见 index.mjs 的updateBaseline),所以随手执行会无意吞掉新违规;
  4. CI 永远不会自动刷新基线(见 SKILL.md),杜绝"CI 跑挂就自动放行"的路径。

另外注意一个边界:expired-exception与基线是两条独立的豁免机制——例外写在architecture-policy.yaml的exceptions里并带过期时间,基线写在.architecture-baseline.json里且不设过期。它们都只能通过评审后的显式操作变更,禁止通过新增 lint disable 规避(disable-count规则会拦截这类行为)。

五、检查器已知局限:相对导入解析边界

原文档最后给出了一条重要的使用提示:当前检查器只解析相对导入(relative imports)。这一事实可以直接在 policy.mjs 的resolveImport中验证:

  • 只有以.开头的 specifier 才会被当作可解析导入;
  • 解析时会尝试SOURCE_EXTENSIONS内的扩展名与index.*两种形态;
  • 其他(如 workspace 包名、路径别名、动态 import)直接返回null并跳过。

因此:

  • workspace 别名导入与包级导入需要单独人工检查;
  • 即便architecture:check全部通过,也不代表每一个依赖关系都被分析到了;
  • 检查器通过 TypeScript AST(ts.isImportDeclaration/ts.isExportDeclaration/ts.isImportEqualsDeclaration)收集导入语句,动态require()或字符串拼接的导入路径也不在覆盖范围内。

理解这条边界能避免一个常见误判:报告"通过"≠"没有跨模块边",只等于"相对导入层面没有发现违规"。原文档的提醒应当作为排查时的默认心法。

六、完整排查路径与命令速查

把以上内容串成一条可执行的排查流程:

  1. 复现报告:pnpm architecture:check --changed(增量)或pnpm architecture:check(全量),相关脚本定义在根 package.json 的architecture:*系列命令中(architecture:check/architecture:report/architecture:baseline:update/architecture:context);
  2. 分类违规:对照 rule-catalog.md 与本文第二节,区分规则级问题与工作流级问题;
  3. 读取上下文:pnpm architecture:context <module-id>输出该模块的 owner、requires、公开入口、直接依赖契约与边界约束,作为修复前的阅读包(实现见 index.mjs);
  4. 修复并复查:按第二节方案修复后再次运行--changed,确认新增违规归零、基线违规数量未变;
  5. 评审例外:只有经过明确评审的基线变更才执行pnpm architecture:baseline:update,过期例外一律先解决底层违规再删除,不做静默延期。

命令速查表:

场景命令
增量检查(含未跟踪文件)pnpm architecture:check --changed
全量扫描(含已提交改动)pnpm architecture:check
生成模块上下文阅读包pnpm architecture:context <module-id>
输出 JSON/Markdown 报告pnpm architecture:report [--markdown]
评审后的基线更新pnpm architecture:baseline:update

总结

ZCode 的架构治理把"架构意图"落成可执行策略与静态检查:规则级违规(module-dependency、deep-import、cycle、missing-module-artifact、expired-exception)都有明确的修复路径,变更范围由 git 差异加反向依赖闭包决定,基线通过指纹机制区分存量与新增违规且只在评审后更新,而相对导入解析的边界决定了"通过"结论的适用范围。把这套排查方法内化后,你不仅能快速消除pnpm architecture:check的报错,还能在动手改代码之前就主动把模块边界、依赖声明与公开契约设计到位。

  • 人工智能
  • 大模型
  • 代码智能体
  • AI Agent
  • 桌面应用
  • 后端
  • 前端
  • CLI

【免费下载链接】ZCode

ZCode 是 AI 编程工作台,提供桌面应用、浏览器界面和终端 Agent。本仓库包含客户端、后端服务、共享 UI,以及 Agent CLI 与运行时源码。

项目地址:https://gitcode.com/zai-org/ZCode
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询