| name | ddev-arch |
| description | 【禁止自动加载 — 不可被「技能强制评估」自动匹配】仅限用户手动调用。初始化项目级长期架构规范文档,建立模块边界、依赖规则和变更门禁,约束后续代码修改范围。 |
项目架构规范初始化
定位
这是项目级架构边界契约,目标是让 AI(和人)在修改代码前知道:模块职责是什么、边界在哪里、哪些依赖允许、哪些改动会破坏架构。
- 每个模块独立成文,必须讲清楚:模块负责什么、不能负责什么、允许依赖谁、对外暴露什么
仓库目录映射 服务于架构约束:帮助 AI 把改动限制在正确模块内
- 文档要能回答"这次代码改动是否越界",而不是只回答"代码在哪里"
与实现架构的区别
- 本 skill:初始化项目级长期架构规范,产出稳定的模块边界、依赖规则和变更门禁。它约束后续所有代码修改。
ddev-spec:针对某一次代码改动,编写临时实现架构文档。它必须读取并遵守本 skill 产出的项目级架构规范。
触发条件
- 新项目启动,需要建立长期架构规范
- 现有项目缺少架构约束文档,需要补建
- 用户明确要求"写架构文档"且不是针对某次代码改动
- 用户希望约束 AI 后续改代码范围,避免破坏既有架构
探索要求
- 先并行分发多个子代理,从目录结构、构建目标、入口调用链、公共接口和数据所有权等视角探索仓库,收集模块边界候选。
- 再根据识别出的功能模块,并行分发多个子代理分别深入探索各模块,补齐职责、边界、对外接口、依赖方向、状态所有权和关键数据结构。
- 汇总所有子代理结果后,再统一写架构总览、模块规范文档和
AGENTS.md,不要在探索完成前提前定稿。
基本流程
- 按"探索要求"探索仓库,先判定是否为多独立代码单元仓库(见"模块边界识别"规则 0),再形成模块候选、职责候选和依赖候选
- 多单元仓库:先写顶层
00_仓库集成架构.md(按"仓库集成架构模板"),再为每个独立代码单元写各自的 01_架构总览.md 和模块文档。单单元仓库:直接写 01_架构总览.md(按"单单元架构总览模板")。技术栈字段必须填写实际检测到的语言,后续步骤以此为分流依据
- 为每个模块写独立规范文档,按"模块规范模板"编写;多单元仓库时模块文档放在对应单元的子目录下
- 需要图示时,按 ddev-diagram 规范手写 ASCII 到
.md,图附近标注用途说明
- 项目依赖与封装审计:根据各单元
01_架构总览.md 中声明的技术栈,按 lang-audit-matrix.md 分流到对应的审计策略(详见下方"项目依赖与封装审计"章节)。多单元仓库时,每个单元独立产出审计文档(放在各自子目录下)
- 构建变体与环境审计:根据各单元
01_架构总览.md 中声明的技术栈,按 lang-audit-matrix.md 分流到对应的审计策略(详见下方"构建变体与环境审计"章节)
- 按"AGENTS.md 规则注入"章节的写入策略,将架构规范读取和变更门禁规则写入仓库根目录的相应配置文件(
AGENTS.md 或 CLAUDE.md)
- 全部完成后做验收检查(见"验收标准")
模块边界识别
按以下优先级判定模块边界:
- 独立代码单元检测(最高优先级):先判断仓库是否包含多套独立编译、无源码交叉引用的代码单元——
- 异构多核:同一芯片的不同核心(如 ARM A7 + RISC-V E907),各自有独立 OS、独立构建系统和入口
- 多服务/多应用:同一仓库下多个独立服务或应用,各自有独立构建配置(
CMakeLists.txt、Makefile、go.mod、Cargo.toml 等)且互不直接源码依赖
- 多固件镜像:同一产品线多个独立固件镜像(如 bootloader + RTOS + Linux kernel),各自独立构建
检测方法:扫描仓库顶层目录,若存在多个不共享构建系统的独立子目录(各自包含独立入口和构建配置),且它们之间无源码级
#include/import 交叉引用,则判定为多个独立代码单元。多单元场景下,每个单元独立产出架构文档(各自的结构总览+模块边界),顶层另出 00_仓库集成架构.md 描述单元间的交互、数据流和联合构建关系。单单元场景直接走后续 1-7 条规则检测模块边界。
- 公开接口边界:头文件、导出类、路由、命令、服务接口、包入口等对外暴露面,优先作为模块边界依据
- 目录即模块:如果仓库已经按目录划分了子系统/组件,直接以目录为模块单位
- 数据所有权:谁拥有关键状态、配置、缓存、设备句柄、数据库表或领域对象,谁就是对应职责的主模块
- 入口文件回溯:找
main()、app init、注册函数等入口,反向追踪调用链来聚合文件
- 独立构建目标:如果某目录有独立的
CMakeLists.txt、Makefile、Cargo.toml 等,就是一个模块
- 平铺文件兜底:如果几十个文件平铺在一个目录里,按功能前缀分组(如
uart_*.c、spi_*.c、auth_*.py、handler_*.go),每组为一个模块
- 嵌套处理:模块内若有明显的子模块边界(如
module/sub_a/、module/sub_b/),子模块独立成文,编号用两级序号(如 02-01_子模块名.md)
不确定边界时,宁可先拆小并写明"边界待确认",不要把多个职责合成一个大模块。
文档目录规划
所有模块统一单文件,只包含架构约束内容。
单代码单元(默认)
仓库只包含一套代码时,采用扁平结构:
docs/architecture/
├── 01_架构总览.md # 项目简介、架构原则、依赖规则、变更门禁、模块列表
├── stdlib-wrappers.md # [C/C++ 项目] 标准库函数替代封装 API 参考
├── dependency-audit.md # [非 C 项目] 依赖审计报告
├── modules/
│ ├── 02_<模块名>.md # 模块约束:职责+边界+接口+依赖+状态所有权+修改范围+检查清单
│ ├── 03_<模块名>.md
│ └── 03-01_<子模块名>.md # 子模块
└── diagrams/ # 架构图文件(手写 ASCII 到 .md)
└── *.md
多独立代码单元
仓库包含多套独立编译、互不源码依赖的代码单元时(异构多核、多服务/多应用、多固件镜像等),每单元独立出架构文档,顶层放单元间集成架构:
docs/architecture/
├── 00_仓库集成架构.md # 各单元间的交互关系、数据流、通信协议、联合构建与部署
├── <unit_a>/ # 独立代码单元 A(以单元实际目录名或简称命名,如 linux-4.9、melis-v3.0)
│ ├── 01_架构总览.md
│ ├── stdlib-wrappers.md # 或 dependency-audit.md(按各单元语言独立审计)
│ ├── modules/
│ │ └── 02_<模块名>.md
│ └── diagrams/
├── <unit_b>/ # 独立代码单元 B
│ ├── 01_架构总览.md
│ ├── modules/
│ └── diagrams/
└── ...
判定标准:独立代码单元 = 有独立的构建系统入口 + 不与其他单元的源码发生 #include/import 交叉引用。各单元的依赖审计(stdlib-wrappers.md 或 dependency-audit.md)在各自子目录下独立产出。
项目依赖与封装审计
在写架构文档之后,必须对工程的依赖和封装做审计。审计策略依赖 01_架构总览.md 中声明的技术栈。
⚠️ 硬门禁:审计前必须先读取 lang-audit-matrix.md
进入分流决策前,必须用 Read 读取 references/lang-audit-matrix.md,根据技术栈确定审计策略。禁止凭记忆或猜测选择审计方案。
分流决策
主 agent 读取 01_架构总览.md 的「技术栈」字段,判断项目语言:
- C 项目 → 执行下方「C 语言标准库替代函数审计」(完整流程,2 轮 + stdlib-wrappers.md)
- C++ 项目 → 执行 C 标准库审计子集 + STL 策略审计
- Rust / Go / Python / TS / JS / Java → 执行「非 C 语言依赖审计」(简化流程,单轮 + dependency-audit.md)
- Generic → 记录依赖清单文件路径,不做深度审计
C 语言标准库替代函数审计
当项目语言为 C 时执行。目标:让后续所有编码和审查工作都知道"这个工程对每个标准库函数有没有自己的封装、有的话应该用哪个"。
审计目标仅为发现工程封装,产出 API 参考文档,不调查裸调现状。
审计流程(反复审查直到无新发现)
第一轮:全量扫描
主 agent 拉独立子代理,按以下搜索范围全量扫描工程:
搜索范围(按函数类别):
| 类别 | 标准库函数 |
|---|
| 内存分配 | malloc, calloc, realloc, free |
| 字符串操作 | strcpy, strncpy, strcat, strncat, strlen, strcmp, strncmp, strstr, strchr, strrchr, strdup |
| 格式化 | sprintf, snprintf, vsprintf, vsnprintf, sscanf |
| 内存操作 | memcpy, memmove, memset, memcmp, memchr |
| 文件 I/O | fopen, fclose, fread, fwrite, fprintf, fscanf, fgets, fputs, fseek, ftell, remove, rename |
| 标准输出 | printf, puts, putchar |
| 通用工具 | atoi, atol, strtol, strtoul, abs, rand, srand, qsort, bsearch |
| 断言/终止 | assert, abort, exit |
| 时间 | time, localtime, strftime, sleep, usleep |
子代理搜索方法:
- 在工程头文件(
*.h)和源文件(*.c)中,对每个标准库函数名做 grep,找到:
- 替代封装(wrapper):记录封装函数名、签名、所在文件和行号
- 对于找到的替代封装,再反向搜索其调用者分布(哪些模块在用这个封装)
- 识别封装的特征:是否添加了边界检查、错误日志、平台适配、返回值校验等增强
- 条件编译门控审计:对每个找到的替代封装,检查其定义和声明是否受条件编译宏(
#if/#ifdef/#if defined())门控:
- 记录门控宏的名称
- 记录门控宏的定义位置(文件 + 行号)
- 判断门控宏的默认开关状态(默认开启 / 默认关闭 / 依赖其他宏)
- 记录开启和关闭时的行为差异(如:关闭时是否有 fallback 实现、是否降级为空操作、是否导致编译错误)
- 第三方库检查:搜索
thirdparty/、third_party/、external/、lib/ 等目录,识别工程中引入的第三方库(如 FatFs、lwIP、mbedTLS、FreeRTOS 等),检查:
- 是否有对应的工程适配文件(
*_port.c、*_conf.h、*_config.h)注入了工程封装
- 记录每个第三方库的适配状态:已适配 / 未适配(需标注风险)
子代理输出格式:
## 标准库替代封装审计 — 第 N 轮
### 已发现的替代封装
| 标准库函数 | 工程封装 | 签名 | 所在文件 | 增强特性 |
|-----------|---------|------|---------|---------|
| malloc | os_malloc | void *os_malloc(size_t size) | src/os/heap.c:42 | 内存池、失败日志 |
| free | os_free | void os_free(void *ptr) | src/os/heap.c:78 | 指针置 NULL |
### 本轮尚未覆盖的标准库函数
| 函数 | 原因(未搜索到 或 搜索结果不明确) |
|------|----------------------------------|
第二轮及以后:主代理审查 + 补充搜索
- 主代理审查子代理输出:
- 检查是否有遗漏的函数类别
- 检查子代理的输出中"尚未覆盖"列表是否合理
- 标注需要进一步确认的项目
- 主代理补充搜索:对子代理标注的不确定项,主代理亲自搜索确认
- 主代理拉新子代理做第二轮:
- 针对第一轮遗漏或不确定的类别,做更精确的搜索
- 搜索范围可以缩小到特定目录或文件
- 新子代理的输出格式同上
- 循环终止条件:
- 最近一轮子代理输出中"尚未覆盖的标准库函数"列表已验证且无遗漏
- 主代理确认所有不确定项已解决
- 至少完成 2 轮(第一轮全量 + 至少一轮补充)
产出文档:stdlib-wrappers.md
审计完成后,将所有发现合并写入 docs/architecture/stdlib-wrappers.md:
# 标准库函数替代封装参考
> 最后审计时间:<日期>
> 审计轮次:<N> 轮
> 本文档由 ddev-arch 标准库审计流程生成,后续所有代码编写和审查必须以此为参考。
## 内存分配
| 标准库函数 | 工程封装 | 签名 | 所在文件 | 说明 |
|-----------|---------|------|---------|------|
| malloc | `os_malloc` | `void *os_malloc(size_t size)` | `src/os/heap.c:42` | 内存池分配,自动对齐 8 字节 |
| free | `os_free` | `void os_free(void *ptr)` | `src/os/heap.c:78` | 回收到内存池,自动置 NULL |
| calloc | `os_calloc` | `void *os_calloc(size_t n, size_t size)` | `src/os/heap.c:65` | 封装 calloc + 零初始化校验 |
| realloc | —(无封装,禁止使用) | — | — | 工程内存池不支持 realloc |
## 字符串操作
| 标准库函数 | 工程封装 | 签名 | 所在文件 | 说明 |
|-----------|---------|------|---------|------|
| ... | ... | ... | ... | ... |
## 格式化
...
## 文件 I/O
...
## 断言
| assert | `MOD_ASSERT` | `#define MOD_ASSERT(expr)` | `include/common/mod_assert.h:15` | 失败时写日志 + 复位 |
## 条件编译门控
> 以下列出所有替代封装依赖的条件编译宏。**使用封装前必须确认对应宏已开启**,否则封装可能降级为 fallback 实现、空操作或导致编译错误。
| 封装函数 | 门控宏 | 定义位置 | 默认状态 | 开启时行为 | 关闭时行为 |
|---------|--------|---------|---------|-----------|-----------|
| `os_malloc` | `USE_OS_HEAP` | `src/config.h:12` | ✅ 开启 | 内存池分配,自动对齐 | fallback 到标准 `malloc` |
| `os_free` | `USE_OS_HEAP` | `src/config.h:13` | ✅ 开启 | 回收到内存池,自动置 NULL | fallback 到标准 `free` |
| —(示例,按实际工程填写) | — | — | — | — | — |
### 门控宏速查表
| 门控宏 | 默认值 | 影响范围 | 定义文件 |
|--------|--------|---------|---------|
| `USE_OS_HEAP` | 开启 | 内存分配/释放全部封装 | `src/config.h:12` |
| —(示例,按实际工程填写) | — | — | — |
## 第三方库适配状态
| 第三方库 | 版本 | 源码目录 | 适配方式 | 适配状态 | 备注 |
|---------|------|---------|---------|---------|------|
| FatFs | R0.14b | `thirdparty/fatfs/` | `ffconf.h` 配置宏 → `os_malloc/os_free` | ✅ 已适配 | — |
| mbedTLS | 3.4.0 | `thirdparty/mbedtls/` | `mbedtls_config.h` → `os_calloc/os_free` | ✅ 已适配 | — |
| lwIP | 2.1.2 | — | — | ⚠️ 未使用 | — |
### 未适配第三方库风险
如果第三方库适配状态为"未适配":
- 该库的内存分配/文件 I/O/断言可能绕过工程封装体系
- 必须在此标注风险等级和允许的例外范围
- 后续审查中如果发现该库裸调标准库,视为已知风险,不阻塞但必须追踪
强制约束
- 审计未完成前,不得进入下一步(机型宏扫描)
- 至少完成 2 轮搜索才能定稿
- 最终文档必须覆盖上表中列出的所有标准库函数类别
- 如果某个类别整个工程均未使用(如文件 I/O),也必须在文档中标注"本工程未使用此类函数"
- 所有替代封装必须审计条件编译门控宏,记录宏名、定义位置、默认状态和行为差异
stdlib-wrappers.md 必须包含「条件编译门控」章节和门控宏速查表
非 C 语言依赖审计
当项目语言为 C++ / Rust / Go / Python / TS / JS / Java 时执行。目标:记录依赖清单、识别风险依赖、标注关键封装。
审计流程(单轮)
- 识别依赖清单文件:定位包管理器配置文件(
Cargo.toml、go.mod、pyproject.toml、package.json、pom.xml 等)
- 提取依赖树:解析配置文件,提取直接依赖和版本约束。如果依赖数量 > 50,只记录直接依赖
- 扫描标准库替代封装:在项目源码中搜索对标准库函数的封装——
- C++:自定义 allocator、智能指针策略、异常/RTTI 策略
- Rust:
unsafe 封装、自定义 allocator、no_std 策略
- Go:
unsafe/cgo 使用、标准库替代封装
- Python:C 扩展模块、标准库函数封装(如
os.path 封装)
- TS/JS:polyfill 策略、Node.js API 版本依赖
- Java:反射/JNI 使用、标准库替代封装
- 标注风险依赖:已废弃、有已知漏洞、版本过旧的依赖
- 产出
dependency-audit.md:包含依赖树 + 关键封装清单 + 风险依赖标注
产出文档:dependency-audit.md
# 项目依赖审计
> 最后审计时间:<日期>
> 项目语言:<语言>
> 本文档由 ddev-arch 依赖审计步骤生成。
## 依赖清单
| 包名 | 版本 | 类型 | 用途 | 风险 |
|------|------|------|------|------|
| ... | ... | direct/dev | ... | — / ⚠️ deprecated / 🔴 CVE-xxxx |
## 标准库替代封装
<!-- 列出项目中对标准库的封装函数,无则标注"本项目未使用标准库替代封装" -->
| 标准库函数/类 | 工程封装 | 所在文件 | 增强特性 |
|-------------|---------|---------|---------|
## 风险依赖
<!-- 列出已废弃、有已知漏洞、或版本过旧的依赖 -->
| 包名 | 当前版本 | 风险类型 | 建议操作 |
|------|---------|---------|---------|
强制约束
- 非 C 语言审计为单轮,不要求 2 轮循环验证
- 依赖清单至少覆盖所有直接依赖
- 如项目无任何第三方依赖,标注"本项目无第三方依赖"即可
- 如果某个语言的标准库替代封装搜索返回空,标注"本项目未使用标准库替代封装"
Generic 项目依赖记录
当项目语言为 Generic(无法匹配任何已知语言)时:
- 记录能找到的所有包管理配置文件路径
- 记录 vendor / lib / external 等第三方代码目录
- 产出
dependency-manifest.md,仅包含文件路径清单
- 不做深度审计
构建变体与环境审计
在写架构文档之后、注入 AGENTS.md 规则之前,必须审计项目的构建变体和环境配置。审计策略依赖 01_架构总览.md 中声明的技术栈,按 lang-audit-matrix.md 分流。
分流决策
主 agent 读取 01_架构总览.md 的「技术栈」字段,判断:
- 嵌入式 C/C++ 项目(存在
cfg.h、VERSION_MAJOR、#define MODELNAME 等信号)→ 执行下方「机型宏扫描与注入」(完整流程)
- 非嵌入式项目(所有语言)→ 执行「通用构建审计」(简化流程,记录构建命令 + 产物路径 + 环境变量)
机型宏扫描与注入(嵌入式 C/C++ 项目)
扫描步骤
- 搜索所有 cfg.h:
grep -rl "VERSION_MAJOR\|FIRMWARE_VERSION" --include="*_cfg.h",列出所有配置头文件
- 提取每个 cfg.h 中的机型宏:识别所有
#define MODELNAME / //#define MODELNAME 对,或 #define PRINTER_MODEL (N) 枚举值,或 #if defined(MODEL) 分支
- 提取每个机型的编译指令和产物:搜索构建系统(Makefile、SConscript、.bat、.mk)中的
project=、model=、-DMODEL= 等变量,以及输出产物路径(out/、build/、firmware_*.bin)
- 判断仓库类型:
| 特征 | 类型 | 注入位置 |
|---|
每个机型有自己的子目录(如 project/pos/i9/),目录下已有或可以创建 CLAUDE.md | 多机型分目录 | 每个机型子目录的 CLAUDE.md |
单个 Makefile/构建脚本,通过不同宏切换机型(如 PRINTER_MODEL 值) | 单构建多机型 | 仓库根目录的 CLAUDE.md 或 AGENTS.md |
| 单一机型,无分支 | 单机型 | 仓库根目录的 CLAUDE.md 或 AGENTS.md |
注入模板
多机型分目录 — 写入 <机型子目录>/CLAUDE.md
如果子目录下已有 CLAUDE.md 或 AGENTS.md,在合适位置追加机型信息;否则新建 CLAUDE.md:
## <机型名>
- **机型宏**:`<MODEL_MACRO>`(定义在 `<路径>/<文件>:<行号>`)
- **编译指令**:`<构建命令>`
- **编译产物**:`<产物路径>`
- **说明**:<机型用途或特性的一句话说明>
### 代码阅读前置条件
**本目录下的所有代码均以 `<MODEL_MACRO>` 宏启用为前提。** 阅读代码时:
1. 先确认机型宏 `#define <MODEL_MACRO>` 处于活跃状态
2. 所有 `#if defined(<MODEL_MACRO>)` 分支生效,其余 `#elif`/`#else` 分支忽略
3. 机型宏变更后,必须同步更新本文件中的机型宏和编译指令
单构建多机型 — 写入根目录 CLAUDE.md
在架构规范段落之后追加:
## 机型说明
本仓库通过 <选择机制> 切换机型,当前活跃机型为 `<MODEL_MACRO>`。
| 机型宏 | 说明 | 编译指令 | 编译产物 |
|--------|------|----------|----------|
| `<MACRO_A>` | <说明> | `<命令>` | `<路径>` |
| `<MACRO_B>` | <说明> | `<命令>` | `<路径>` |
### 代码阅读前置条件
1. 阅读代码前,必须先确认当前使用的机型宏
2. 所有代码理解以该机型宏 `#define` 为前提,只阅读该机型宏对应的 `#if`/`#elif` 分支
3. 机型宏通过 `<方式>` 切换(如修改 cfg.h 中的 `#define`、修改 Makefile 变量等)
4. **机型宏变更后,必须同步更新本文件中的"当前活跃机型"和相关编译指令**
单机型 — 写入根目录 CLAUDE.md
## 机型说明
- **机型宏**:`<MODEL_MACRO>`
- **编译指令**:`<构建命令>`
- **编译产物**:`<产物路径>`
强制规则
机型宏信息注入 CLAUDE.md 后,以下规则同时写入,成为 AI 的行为约束:
- AI 在阅读任何代码之前,必须先确认当前文件/目录对应的机型宏,并基于该机型宏去理解
#if/#elif 分支。
- 机型宏发生变更时(无论人工修改还是代码生成),必须同步更新对应
CLAUDE.md 中的机型说明、编译指令和产物路径。
- 如果 AI 不确定当前机型宏,必须先查找并确认后再继续。
通用构建审计(非嵌入式项目)
当项目为非嵌入式 C/C++ 或其他语言时执行。目标:记录构建命令、产物路径和环境配置。
审计步骤
- 识别构建系统:定位构建配置文件——
- C/C++:
CMakeLists.txt、Makefile、meson.build、BUILD
- Rust:
Cargo.toml([profile] 段)
- Go:
go.mod + Makefile(如有)
- Python:
pyproject.toml([project.scripts] 段)、setup.py、Makefile
- TS/JS:
package.json(scripts 段)、webpack.config.js、vite.config.ts
- Java:
pom.xml、build.gradle
- 提取构建命令:从构建系统推断标准构建命令(如
cmake --build build、cargo build --release、go build ./...)
- 记录产物路径:识别输出目录(
build/、dist/、target/、out/)
- 记录环境配置:如果存在
.env、.env.example、config/、environments/ 等,记录关键环境变量和配置切换方式
- 写入
CLAUDE.md:在根目录 CLAUDE.md 中追加简化版构建说明
注入模板(根目录 CLAUDE.md)
## 构建说明
- **构建系统**:<CMake / Cargo / Go / ...>
- **构建命令**:`<标准构建命令>`
- **编译产物**:`<产物路径>`
- **环境变量**:
| 变量名 | 说明 | 默认值 |
|--------|------|--------|
| ... | ... | ... |
如果项目无多环境/多变体,简化为:
## 构建说明
- **构建命令**:`<命令>`
- **编译产物**:`<路径>`
AGENTS.md 规则注入
所有架构文档生成后,按以下策略写入架构规范读取和变更门禁规则(按实际模块名和路径调整):
- 仓库根目录已有
AGENTS.md -> 更新 AGENTS.md
- 仓库根目录已有
CLAUDE.md -> 更新 CLAUDE.md
- 两者都不存在 -> 创建
CLAUDE.md(不创建 AGENTS.md)
- 两者同时存在 -> 只更新已有的文件,不新增
以下为写入内容模板:
<!-- 由 ddev-arch 自动生成 -->
## 项目架构规范
本仓库已建立项目级架构规范,位于 `docs/architecture/`。
<!-- 多单元仓库时追加:仓库包含 <N> 个独立代码单元,先读 00_仓库集成架构.md 了解整体关系。 -->
### 修改代码前
<!-- 多单元仓库时:先确认代码所属的代码单元,再读该单元的架构文档 -->
<!-- 单单元仓库时直接引用 01_架构总览.md -->
- 先读架构总览文档的"依赖规则"和"架构变更门禁"
- 读对应模块的约束文件(按实际路径:`modules/0X_<模块名>.md` 或 `<单元>/modules/0X_<模块名>.md`)
- 修改必须优先落在模块约束标注的"允许修改范围"内,不要因为路径相近就跨模块改代码
- 如果实现需要突破模块边界、增加反向依赖、移动文件、改变公共接口或迁移状态所有权,必须先更新架构文档,再改代码
### 架构变更门禁
以下变化视为架构变更,不能只改代码:
- 新增、删除、合并或拆分模块
- 改变模块职责或数据所有权
- 新增跨模块依赖,尤其是反向依赖、循环依赖或下层依赖上层
- 修改公共接口、协议、持久化格式、构建产物或外部可见行为
- 将原本私有的状态、函数或文件提升为跨模块共享
<!-- 项目语言为嵌入式 C/C++ 时,追加机型说明段(由"构建变体与环境审计"步骤写入) -->
<!-- 其他语言时,追加构建说明段(由"构建变体与环境审计"步骤写入) -->
对于需要写入根目录 CLAUDE.md 的机型说明(单构建多机型或单机型),在上述模板之后追加"机型宏扫描与注入"章节中对应类型的模板。
如果目标文件(AGENTS.md 或 CLAUDE.md)已存在,检查是否已有架构规范相关段落,有则更新内容,无则追加在合适位置。同样检查是否已有机型说明段落,有则更新,无则追加。
架构总览模板
01_架构总览.md 按以下结构编写:
# 项目架构总览
## 项目简介
- 项目名称:<名称>
- 技术栈:<语言/框架/平台>
- 一句话定位:<这个项目是干什么的>
## 架构原则
<!-- 写 3-7 条会真实约束改代码的原则,不写空泛口号 -->
- <原则1:例如 UI 层不得直接访问持久化层>
- <原则2:例如 设备状态只归 Driver Context 持有>
## 模块全景图
<!-- 展示最终 ASCII 图;图前标注用途 -->
## 模块列表
| 序号 | 模块名 | 约束文件 | 职责 | 允许依赖 |
|------|--------|---------|------|----------|
| 02 | <模块A> | [02_<模块A>.md](modules/02_<模块A>.md) | <职责> | <模块B> |
| 03 | <模块B> | [03_<模块B>.md](modules/03_<模块B>.md) | <职责> | — |
## 依赖规则
<!-- 写清允许方向、禁止方向、是否允许跨层调用、公共接口归属 -->
- 允许:`<模块A>` → `<模块B>`
- 禁止:`<模块B>` → `<模块A>`
- 公共接口必须定义在:`<路径>`
## 架构变更门禁
- 新增跨模块调用前,必须更新对应模块约束文件
- 改变核心状态所有权前,必须更新对应模块约束文件
## 仓库顶层目录结构
- `src/` - 主源码,包含模块 <A>、<B>
- `include/` - 公共头文件
- `tests/` - 测试代码
仓库集成架构模板
当仓库包含多个独立代码单元时,在顶层 docs/architecture/ 下产出 00_仓库集成架构.md,按以下结构编写:
# 仓库集成架构
## 仓库简介
- 仓库名称:<名称>
- 代码单元数量:<N> 个
- 仓库类型:<异构多核 / 多服务 / 多固件镜像 / 其他>
## 代码单元清单
| 单元名 | 架构文档路径 | 技术栈 | 构建系统 | 说明 |
|--------|-------------|--------|---------|------|
| <unit_a> | [<unit_a>/01_架构总览.md](<unit_a>/01_架构总览.md) | <C/Linux Kbuild> | `make` | <一句话说明该单元做什么> |
| <unit_b> | [<unit_b>/01_架构总览.md](<unit_b>/01_架构总览.md) | <C/Melis RTOS> | `make` | <一句话说明该单元做什么> |
## 单元间交互关系
<!-- ASCII 图展示各单元的交互关系,标注通信协议、数据流向 -->
### 通信方式
| 从(单元) | 到(单元) | 方式 | 协议/接口 | 说明 |
|-----------|-----------|------|----------|------|
| <unit_a> | <unit_b> | IPC / 网络 / 共享内存 / 文件 | <协议名> | <说明> |
### 数据流
<!-- 描述关键数据在各单元间的流动路径 -->
## 联合构建与部署
- 构建顺序:<先构建 A,再构建 B>
- 产物聚合方式:<打包脚本 / 烧录工具>
- 联合产物路径:`<路径>`
## 跨单元约束
<!-- 某单元的变更可能影响另一单元的场景 -->
- <unit_a> 的 <接口/协议> 变更会影响 <unit_b> 的 <功能>
模块规范模板
所有模块统一使用单文件模板,只包含架构约束内容。
# <模块名>
## 模块职责
<一句话说清这个模块负责什么>
## 职责边界
- 负责:<本模块必须承担的职责>
- 不负责:<容易误放进本模块、但不属于本模块的职责>
## 对外接口
- 头文件/导出入口:`src/<路径>/xxx.h`
- 公开函数/类/命令:`<symbol>`
- 外部可见行为:<协议/文件/接口/构建产物>
## 仓库目录映射
- 主目录:`src/<实际路径>/`
- 核心文件:
- `src/<路径>/xxx.c` - <文件职责>
- `src/<路径>/xxx.h` - <头文件职责>
- 测试目录:`tests/<路径>/`
## 允许依赖
- 可依赖:`<模块B>`,原因:<为什么允许>
- 被依赖:`<模块C>` 会调用本模块的 `<接口>`
- 禁止依赖:`<模块D>`,原因:<会造成反向依赖>
## 状态与数据所有权
- 本模块拥有:<状态/结构体/配置/缓存/设备句柄>
- 本模块只读:<来自其他模块的数据>
## 允许修改范围
- 可以改:<模块内实现文件/私有函数/局部数据结构>
- 谨慎改:<公共接口/共享结构体/配置格式>
- 必须先更新架构文档再改:<职责迁移/依赖新增>
## 修改前检查清单
- 本次改动是否仍在"允许修改范围"内
- 是否新增了模块依赖或改变依赖方向
- 是否改变了公共接口、外部行为或状态所有权
子模块模板
若模块内有子模块边界,子模块独立成文,使用同上模板,编号用两级序号(如 02-01_子模块名.md)。
图怎么画
⚠️ 硬门禁:画图前必须先加载 ddev-diagram
在任何 ASCII 图动笔之前,必须执行 Skill("ddev-diagram") 加载绘制规范。加载完成前禁止写任何 ASCII 图到 .md。
加载后严格遵守:
- Unicode 制表符(┌─┐└┘│├┤┬┴┼),禁止 ASCII 拼接(+/-/|)
- 框内标签英文(CJK 字符宽度不可靠)
- 正交连线,禁止斜线
- 右边框对齐、上下等宽、无断线
优先画:
- 系统边界图(组件图):看清系统与外部的关系
- 模块依赖图(组件图):看清允许依赖方向和禁止方向
- 状态所有权图:看清关键状态属于哪个模块
验收标准
初始化完成后,逐项检查:
通用检查项(所有语言必须通过)
| 检查项 | 标准 |
|---|
| 模块覆盖 | 所有可识别模块都有对应文档,无遗漏 |
| 职责边界 | 每个模块约束文件都写清"负责"和"不负责" |
| 依赖约束 | 总览和模块约束文件都写清允许依赖与禁止依赖 |
| 状态所有权 | 关键状态、配置、缓存、句柄都有明确归属 |
| 修改门禁 | 每个模块约束文件都有"允许修改范围"和"修改前检查清单" |
| 路径可定位 | 每个模块的"仓库目录映射"指向真实存在的目录/文件 |
| 图完整 | 架构总览有模块依赖图 |
| 交叉引用正确 | 模块间依赖关系在文档中有标注且路径正确 |
| 一致命名 | 模块名、文档名、图中标签三者一致 |
| 配置文件注入 | 仓库根 AGENTS.md 或 CLAUDE.md(按写入策略)含架构规范索引和变更门禁 |
| 语言声明 | 各单元 01_架构总览.md(单单元或多单元)的技术栈字段已填写实际检测到的语言 |
| 构建信息 | 构建命令、产物路径已写入对应 CLAUDE.md(按语言策略) |
多独立代码单元专属检查项
| 检查项 | 标准 |
|---|
| 单元清单 | 00_仓库集成架构.md 已列出全部独立代码单元,无遗漏 |
| 交互关系 | 仓库集成架构已描述单元间通信方式、数据流和联合构建部署 |
| 跨单元约束 | 仓库集成架构已标注跨单元变更影响的约束规则 |
| 单元间路径 | 所有交叉引用路径正确指向各单元子目录下的文档 |
C/C++ 项目专属检查项
| 检查项 | 标准 |
|---|
| 标准库审计 | stdlib-wrappers.md 已生成,至少 2 轮审计,覆盖全部函数类别,无遗漏 |
| 标准库审计质量 | 所有替代封装签名准确,所在文件路径和行号正确 |
| 条件编译门控 | 所有替代封装的条件编译门控宏已审计,stdlib-wrappers.md 含「条件编译门控」章节和门控宏速查表 |
| 第三方库适配 | 所有引入的第三方库均已在审计表中记录适配状态;未适配的已标注风险 |
嵌入式 C/C++ 项目专属检查项
| 检查项 | 标准 |
|---|
| 机型宏注入 | 各机型子目录 CLAUDE.md 或根 CLAUDE.md 含机型宏、编译指令、产物路径和代码阅读前置条件 |
| 机型宏完整性 | 所有 cfg.h 中定义的机型宏均已被扫描和记录,无遗漏 |
非 C 项目专属检查项
| 检查项 | 标准 |
|---|
| 依赖审计 | dependency-audit.md 已生成,依赖清单已解析,直接依赖无遗漏 |
| 依赖完整性 | 包管理器配置文件已解析,关键依赖和风险依赖已标注 |
文档维护
架构规范不是一次性的。以下情况必须更新:
- 新增/删除/合并/拆分模块时,更新对应单元的架构总览模块列表,新增/删除对应模块文档,同步更新
AGENTS.md 或 CLAUDE.md(按写入策略判断目标文件)
- 新增/删除独立代码单元时,更新
00_仓库集成架构.md 的单元清单和交互关系,新增/删除对应单元子目录下的全套架构文档(仅多单元仓库)
- 独立代码单元间通信协议或交互关系变更时,更新
00_仓库集成架构.md 的"单元间交互关系"和"跨单元约束"(仅多单元仓库)
- 模块文件迁移时,更新对应模块约束文件的"仓库目录映射"
- 模块职责发生变化时,更新约束文件的"模块职责"和"职责边界"
- 新增跨模块依赖时,更新总览的"依赖规则"和相关模块约束文件的"允许依赖"
- 状态、配置、缓存、句柄或关键数据结构所有权变化时,更新约束文件的"状态与数据所有权"
- 修改公共接口、协议、持久化格式或外部行为时,更新约束文件的"对外接口"和"允许修改范围"
- 新增/删除/变更标准库替代封装时,必须同步更新
stdlib-wrappers.md 的对应条目(仅 C/C++ 项目)
- 机型宏变更时(切换机型、新增机型、修改机型宏名),必须同步更新对应
CLAUDE.md 中的机型说明、编译指令和产物路径(仅嵌入式 C/C++ 项目)
- 新增机型时,在对应的
CLAUDE.md 中追加新机型的机型宏、编译指令和产物路径说明(仅嵌入式 C/C++ 项目)
- 新增/删除/升级依赖时,必须同步更新
dependency-audit.md 的对应条目(非 C 项目)
- 构建系统或环境配置变更时,必须同步更新
CLAUDE.md 中的构建说明(所有项目)
完成后
架构规范初始化完成后,不必自动进入实现工作流。仅在用户后续发起具体代码改动时,才按需要触发 ddev-spec,并让该实现文档显式遵守 docs/architecture/ 中的项目级约束。