Install
openclaw skills install archunit-architecture-guard扫描既有 Java/Kotlin 工程,识别其架构模式(分层/MVC、DDD、六边形/端口适配器、整洁架构、按功能分包/模块化单体、或 Android MVVM),报告架构坏味道并给出具体修复指导,再生成 ArchUnit 守护测试把"应有的架构"固化下来,使后续(通常是 AI 生成的)代码无法让它腐烂。当用户想要"守护/约束/保护/测试"某个 Java/Kotlin 工程的架构、提到 ArchUnit、架构适应度函数(architecture fitness function)、依赖规则、越层、包循环、架构腐烂(architecture erosion/rot),或想阻止 AI 生成的代码破坏既有设计时,使用本 Skill。当用户问"这个工程用的什么架构""我的架构干净吗",或想给一个被 AI 反复修改的代码库加护栏时,也应触发。
openclaw skills install archunit-architecture-guard说明:真正生效的触发条件写在文件顶部 frontmatter 的
description字段里(那是 Claude 判断是否加载本 Skill 的唯一依据)。本小节是给维护者看的人类可读版,二者应保持一致;改了触发条件请同步更新description。
适用:
不适用:
AI 生成的代码会漂移。每一次单独的改动看起来都合理,但累积许多次之后,controller 开始直接调 repository,领域模型导入了 Spring,包之间冒出了循环。代码库就这样一次一个"看似合理的 diff"地腐烂下去。
本 Skill 把工程应有的架构翻译成可执行测试,使用 ArchUnit,让新代码一旦破坏规则,构建立即失败。工作流分四个阶段:
锚定原则——守护的是"回归",不是"历史"。 用户几乎从不希望构建在第一天就因为历史债务而失败。ArchUnit 的 freeze(...) 会把当前违规记成基线,只对新增违规报错。这正是"阻止 AI 把架构改得更差"这个场景的命门。默认采用冻结;把严格模式作为可选项提供。
两种规则推导方式——必须明确说明你在用哪种:
freeze 它们,使存量例外不阻塞构建、但不允许任何新增。务必告诉用户哪些规则是描述性、哪些是规定性,让他们清楚自己在约束什么。
所有面向用户的分析内容(架构报告、坏味道发现、修复指导)使用用户在对话中所用的语言。Java 标识符、包名、代码注释保持英文。生成的测试代码本身与语言无关。
目标:在做任何判断之前先建立一份客观的工程地图。不要凭单个类去猜,要收集证据。
pom.xml / build.gradle(.kts) / settings.gradle,以及 src/main/java(和 src/main/kotlin)。注意是否为多模块结构。org.junit.jupiter)还是 JUnit 4(junit:junit)。com.android.*、AndroidManifest.xml)。python references/scan_packages.py <源码根> --base <基础包>(输出格式见该脚本头部说明)。若脚本无法运行,再退而用 grep 抓 import 语句推导同样的依赖图,但优先用脚本。controller、web、api、resource、endpoint、service、usecase、application、domain、model、entity、aggregate、vo、valueobject、repository、dao、persistence、mapper、infrastructure、infra、adapter、port、config、dto、client、gateway、viewmodel、view。同时统计注解:@RestController/@Controller、@Service、@Repository、@Component、@Entity、@Configuration。把这些都记成客观事实,由阶段二来解读。
把证据匹配到模式上。输出一个带排名的识别结果,附置信度和具体证据——绝不只给一个光秃秃的标签。报出混合或不一致的结论很常见也完全合理(例如"分层为主,其中两个模块正向按功能分包漂移")。
完整的信号表和判定指南见 references/pattern-detection.md。主要模式概要:
controller → service → repository),有 Spring 构造型注解,自上而下。Java 后端最常见的形态。domain 包含 aggregate/entity/valueobject/domainservice,外加 application 与 infrastructure。领域层意在保持无框架依赖。port(接口)和 adapter(实现:web、persistence、messaging)。适配器向内依赖。orders、billing、catalog),每个功能内部有自己的分层;功能之间不应依赖彼此的内部实现。view、viewmodel、model/repository,配合 LiveData/StateFlow/DataBinding。View ↔ ViewModel ↔ Model;View 不得直接碰 Model,ViewModel 不得引用 Android 的 View/Activity 等 UI 类型。提醒用户 MVVM 主要是客户端(Android)模式——若在服务端看到它,要再三确认。证据混杂时直说,并选取主导模式作为守护规则的基础,把偏离项列为阶段三的候选坏味道。
每条发现都报告:是什么(坏味道)、在哪(包/类)、为何要紧(具体风险)、怎么修(具体重构)。按严重度排序。完整目录和修复模式见 references/smell-catalog.md。高价值检查项:
domain 内部出现 Spring/JPA 注解或导入(破坏 DDD/六边形的隔离,把业务规则耦合到框架)。@Entity 类型被直接当成 API 的请求/响应体,而非用 DTO。web、另一部分在 controller;这正是 AI 改动造成的漂移,也是引入守护测试最有力的理由。common 范畴)。要诚实对待置信度:某个"违规"可能是有意的例外。对边界情况,表述为"先确认意图,再决定修复还是冻结"。
这是最终交付物。可直接复制的规则配方、依赖坐标、JUnit 4/5 模板、冻结/存储配置见 references/archunit-cookbook.md。
流程:
layeredArchitecture()。onionArchitecture()(ArchUnit 内置),或用显式的 noClasses().that().resideInAPackage("..domain..").should().dependOnClassesThat()... 规则约束"只能向内"的依赖方向。slices().matching(...) 禁止跨功能访问内部实现。slices().matching("<base>.(*)..").should().beFreeOfCycles()。..controller.. 里的类以 Controller 结尾)——这能极低成本地阻止放错位置。java.util.logging、字段注入)。freeze(...),使存量违规变成基线、只有新增才失败。生成 archunit.properties 并解释存储文件(要提交进版本库)。同时提供清晰标注的"严格模式"变体(去掉 freeze(...)),供想对所有违规都报错的团队使用。冻结细节见手册的冻结小节。src/test/java/<base>/architecture/ArchitectureTest.java(或按模块各放一个)。用 @AnalyzeClasses(packages = "<base>", importOptions = ImportOption.DoNotIncludeTests.class)。@ArchTest 规则上方加注释,写明它守护的是哪条坏味道/模式约束,以及它是描述性还是规定性。这样将来某次构建失败时(人或 AI)能看懂为什么失败。mvn test / gradle test)以及首次运行的预期(冻结会记录基线;要提交存储文件)。当用户的工程在磁盘上时,把测试作为真实文件写出来;否则以内联代码/工件形式给出,让用户直接放进工程。
按以下顺序交付,让用户先看到推理、再看到代码:
ArchitectureTest 类、archunit.properties、冻结 vs 严格的说明、运行方式。行文要紧凑。代码和发现才是价值所在,不要围着它们堆评论。
com.example。处处使用真实探测到的基础包。@AnalyzeClasses 范围,或用该模块的基础包;在聚合 pom 上跑单个测试往往会导入错误的类路径。