17 篇文档的变更记录(含修订记录、带编号与中文序号标题)节里只剩一句 「不再记流水账」的占位话,整节删掉,连带它前面的分隔线。 仍保留真台账的只有 5 篇(看门狗 / 配置文件设计 / RAM 预算台账 / 内容页架构 / 本地工具路径方案),外加 scripts/archive 归档区。 份数口径改成可复跑的普查:文档规范 §5.1 补自查命令, stale-doc-count-sweep 的速查表与陷阱各加一条——只 grep 裸标题会漏 带编号与中文序号的两种写法,实测 5 篇只数得出 3 篇,差点据此把 全仓正确的份数改错。 门禁:逐篇与全量 check_doc_links 除 simulator/SDL2 联接假红外 0 问题; skill-size rc=0;表格列数与字节体检相对 HEAD 零新增问题。
6.2 KiB
layer-deps — 分层方向(Drivers/BSP/** 不得 include App/** 的头)
一句话:它只查一条边——下层(BSP)有没有反向依赖上层(App)。它不管同层乱依赖、 不管传递依赖,也不管"这行 include 是不是真的需要"。
- 配置:
entry: python scripts/check_layer_deps.py、pass_filenames: false、files: ^Drivers/BSP/|^scripts/check_layer_deps\.py$|^\.pre-commit-config\.yaml$。 - 规范出处:
docs/架构设计/项目架构总览.md(六层单向:业务 → UI/基础服务 → 中间件 → BSP 驱动 → HAL/RTOS → 硬件)、AGENTS.md分层边界。
1. 它在查什么
- 取
git ls-files的全部文件(不扫构建产物); - 用
App/**下的.h建**按文件名(basename)**的索引; - 扫
Drivers/BSP/**的.c/.h,抓#include "..."形式的引号路径; - 命中 App 头 → 记一条边
(引用方文件, App 头);先按完整路径精确匹配,精确命中为空才退回同名候选; - 头在
ALLOW集合里 → 永久豁免(横切基础设施);否则与BASELINE比对:新增即拦,消失即提示收紧。
ALLOW 三条(新增条目必须先回答"它为什么不是上层服务",否则就是给自己开后门):
App/log/console_log.h(日志后端,BSP 的 dbg_log.h 本来就建在它上面)、
App/util/sys_clock.h(时间源,日志时间戳的唯一提供者)、App/util/sha256.h(纯算法、无状态)。
BASELINE 现存三条(2026-10-22 清点六条 → 2026-10-25 道B 还清 UI 记录三条 → 现三条;只减不增):
NETCFG/net_cfg.c、NETCFG/ftp_cfg.c → App/cfg/cfg_file.h(配置引擎属主是 defaultTask);
NET/lftpd/lftpd_io.c → App/fs/vol_lock.h。
已还清:UI/ui_page_runlog.c、UI/ui_page_selftest.c、UI/ui_w_records.h → App/log/sys_log.h
(2026-10-25 task/UI-rec-reader:ui_w_records 改注入式 ui_rec_reader_t,契约见
docs/UI设计/UI_数据供给设计说明.md §7.2,绑定在 App/task/ui_task.c 与 simulator/main.c)。
修法是注入 / 传参(上层把数据或回调递进 BSP),不是把上层服务搬进 BSP。
2. 为什么要它
-
这句话在两份文档里写了很久,但此前没有任何机械检查:BSP 里可以随手
#include "cfg_file.h"而无人报警。 -
口径漂移是可测量的证据:2026-09 审查报告记"NETCFG 一处",2026-10-22 实测已长到 8 个文件 9 处。边界的静默腐蚀只能靠门禁止住——先钉基线,再只拦新增。
-
实测自证(落地当天):临时给
Drivers/BSP/RS485/rs485.c加一行#include "cfg_file.h"→ 钩子当场 FAIL 并点名该文件与该头(退出码 1),删掉即恢复。挂任何门禁前都该做一次这种注入自证。
3. 触发面
pass_filenames: false:每次都全库扫(反向依赖是全局事实,只看暂存文件会漏)。- 只看
Drivers/BSP/**里的.c/.h;App/**内部谁依赖谁、UI是否越层,一概不看。
4. 报错怎么读
[FAIL] 新增反向依赖(BSP -> App):
- Drivers/BSP/XXX/x.c -> App/cfg/cfg_file.h
- 单独跑:
--verbose列出全部边并标注 豁免/基线/新增;--prune只报已消失、可删的基线条目。 - 退出码 0 通过 / 1 有新增 / 2 用法或环境错误。
- ⚠ pre-commit 会 stash 未暂存改动:改完要先
git add再跑钩子,否则看到的是旧树。
报错时给的是三条修法(按优先级,脚本失败信息里也写着):
- 把上层能力注入给 BSP(函数指针 / 回调)——参照既有先例
net_cfg_set_save_req_cb、net_cfg_request_apply; - 由上层把数据递进来(UI 页面要读记录,就让上层读好再把缓冲传进来);
- 确认它其实是横切叶子能力 → 才谈
ALLOW(见 §7)。
⚠ 本门不是运行时缺陷清单:运行时属主纪律本来就没破(UI 只投递 net_cfg_save_request(),
真正写盘在 defaultTask;模拟器也早在主机侧跑整套逻辑)。它拦的是静态 include 方向这件事本身。
5. 好处
- 把"分层"从 PPT 变成一条命令的事实;债务总量随时可查(
--verbose就是清单)。 - 存量与新增分离,不阻塞正常工作,但保证它只会变小。
6. 限制与盲区
- 只查直接 include,查不到传递依赖。BSP 的
a.h里 include 了 App 头,BSP 的b.c只 includea.h——b.c这条边不出现,但a.h那条会报。原基线ui_w_records.h的 注释专门写着"(组件头,会外溢到 include 链)"(该条 2026-10-25 已还清),就是提醒这种外溢要人来看。 - 只认
#include "引号"形式;#include <App/...>这类写法不在管辖内(也违反 include 规范, 由coding-standard的 CODE-RULE-11 兜)。 - 按 basename 匹配:如果
App/与别处存在同名头,会按同名候选放宽(先精确后同名), 可能把边记到错误的属主上;反向地,如果 App 头改名而 BSP 用别的路径引到它,判定也会变。 - 只管
BSP → App一个方向。App → UI、UI → 驱动内部这类越层不在本门范围。 - 它不看是否真的用到那个头里的东西:删掉最后一处使用却忘了删 include,边仍然存在,
--prune也就不会提示你收紧基线。 git不可用时退回os.walk(跳过.git/.venv/build)——两条路径的清单可能不同, 在无 git 环境下跑出的"通过"不能当证据。- 绿灯的含义只是"没有新增反向边":现存 3 条存量债务仍然在那里(2026-10-25 还清 3 条)。
7. 豁免与基线
BASELINE与ALLOW都写在脚本里(不是外部台账)。只减不增,脚本没有--write。- 两类豁免语义不同,别混:
ALLOW= 永久横切能力(判"它是不是叶子");BASELINE= 待还债(判"这条边什么时候拆")。
8. 改这个门禁本身
- 往
ALLOW加条目要写理由,并想清楚会不会变成"给上层开后门";能进基线就别进ALLOW。 - 还掉一条债就跑
--prune并删掉那一行;基线只减不增,加行等于放弃约束。