B-6 分层收紧(UI_数据供给设计说明.md §7.2 契约原样落地):
- ui_w_records:删 #include "sys_log.h",改 ui_rec_reader_t 只读函数表 +
ui_w_records_bind() 模块级注入(sys_log 读 API 本为全局,函数表共享、
实例读盘缓存仍各自独享——切页陈旧行防御不变);log_id 换不透明编号
UI_W_REC_LOG_RUN/SELFTEST;缓冲容量宏本地化 UI_W_REC_TS/DESC_LEN
(20/29,与 sys_log 契约下限同宽;读失败退化空行防御保留)
- ui_page_runlog / ui_page_selftest:去 sys_log include,实例改不透明编号
- 两端薄包装适配层(编号→syslog_id_t 显式映射,错误码 0/-1/-2 直通不改写):
App/task/ui_task.c 与 simulator/main.c(无头+窗口两个 init 点),均在
ui_menu_init 之前 ui_w_records_bind;未绑定窗口组件按空态防御不读野
- scripts/check_layer_deps.py:BASELINE 6→3(--prune 实测恰 3 条 UI 记录
边消失后写回;剩 NETCFG×2 + lftpd_io 与 UI 无关)——道B 独占收紧口径
- 文档同步:门禁说明 layer-deps.md §1/§6/§9 与 README 计数 6→3;
UI_数据供给设计说明.md §7.2 加落地注记、§14"计划削减"改"已兑现"
- 首提被 id-check 拦下:注释/文档里自造"批卡编号"(B-6/道B-3)命中
"未登记前缀"增量拦截——已全数改回 task-6/道B 普通指称(编号不回收、
禁自造,教训入本提交);id-check 复跑 RC=0(S10 告警为存量非本改动)
验证:
- 模拟器 0 警告;菜单 8 状态 + 内容页 11 页黄金参考逐字节 PASS
(记录页数据流经 reader 后像素与基线一致,无基线更新提交)
- cppcheck/coding-standard/bsp-err/font-charset/no-mojibake 单跑全绿;
layer-deps 复跑:6 处反向 include = 豁免 3 / 基线内 3 / 新增 0,
--prune 显示"基线无需收紧(3 条存量全部仍成立)"
- doc-links --all 的 35 处告警全为本仓检出路径含空格误扫 SDL2 自带 md
(环境问题、非本改动引入;提交门禁按暂存文件名过滤不受影响)
- @build 0 Error 0 Warning(终树复跑 BUILD_OK);uvprojx 无 UV4 抖动、
无新文件不涉注册;其余 38 钩全绿(ui-golden/font-charset/coding-standard/
bsp-err/id-check/cppcheck…单跑复验 RC=0)
增量(A/B 同树 .map,基线 = 合入 c1b425d 后 HEAD):
- Flash:Code +104 B、RO +8 B(两端薄包装代码 + reader 函数表 const)
- RAM:RW +4 B(s_reader 指针)/ ZI +4 B(两页实例 struct 枚举改 uint8
本应 -8 B,链接段对齐后净 +4 B);RW+ZI 合计 +8 B
gate: only mem-budget/mem-numbers pending lead ledger backfill
- pre-commit run --all-files 实测仅这两条失败(台账/RAM 文档 vs .map 漂移,
根因即上列 Code +104/RO +8/RW +4/ZI +4),其余 38 钩全绿;按 Lead 裁决
(task-4 口径"以后照此办理",含 Flash/RAM 漂移同例外)--no-verify 提交,
合并后请主树回填台账(scripts/ram_baseline.py --write 由 Lead 侧执行)
- 手工自查通过:5 个 BOM 文件 BOM=EF BB BF 齐全、docs/py/main.c 保持无 BOM,
全部 10 文件 CR=0、U+FFFD=0
- 台账增量供回填:Flash Code +104 B、RO +8 B;RAM RW +4 B、ZI +4 B
6.5 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分层边界;整改背景见报告 §0.4 的 S-1 行。
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并删掉那一行;基线只减不增,加行等于放弃约束。
9. 变更记录
| 日期 | 变更 |
|---|---|
| 2026-10-23 | 首篇逐项文档(在册 6 条基线逐条列出,含每条的属主背景) |
| 2026-10-25 | 道B(task/UI-rec-reader)还清 UI 记录 3 条:ui_w_records 注入式 reader(§7.2 契约落地),基线 6 → 3 |