Files
STM32F4-Base/AGENTS.md
T
wandering1 e4d70f9fcb docs(真值): 勘查 13 条文档/代码不一致全部处置(10 改 3 核实)+ 补 3 处
证据源 = 主树 ci-artifacts/archify/architecture-dtu-overview-20261030-095132/
evidence.md §11。该目录不入库,故把每条的判据与实测源抄进正文,别让证据只活在
一台机器上。处置口径:能改的改成与代码同源,核实后确认文档没问题的明确写「不改」。

判停点与就绪屏障(条 1、条 3)
- AGENTS 原写 `protect_task.c:98` 直接停走判据:`:98` 是 `s_prev_cal_lock` 变量,
  判停点是 `App/task/protect_task.c:268 if (adc_cal_prot_blocked() != 0u)`,
  判定本体在标定层 `adc_cal_prot_blocked()`、protectTask 只是消费者 → 改引用,
  同步 `ui_task.c:240` 页面注释与 `docs/任务规划/数据可信.md` 的同一处行号。
- `SYS_EVT_FTP_READY` 有生产者(`Src/freertos.c:490`,注释自陈预留)无消费者;
  `SYS_EVT_TIMESYNC_READY` 全仓无生产者也无消费者(只有定义与测试桩)。
  AGENTS、`sys_events.h` 位注释、屏障配图(生成器 + 产物)三处从「四个就绪位
  并列」改成「bit0/bit1 在用 + bit2/bit3 预留」,并指向
  `docs/架构设计/项目启动顺序管理.md` §3.2 的逐位对照表。ftpTask 实际等的是
  `NET|STORAGE`(`Src/freertos.c:477-479`,WaitAll|NoClear,100 ms 分片喂狗)。

/log 不是「三本」(条 5)
- `App/log/sys_log_mcu.c` 的 `s_paths[]` = run / selftest / accid / alarm /
  start / oper / yk 七本 64B 定长文本 + `soe.log`(64B 定长二进制)+ `wdev.log`
  (变长取证行,`App/fs/vol_lock` 侧投,不走定长格式)。
  → AGENTS 两处、总览 §2.1 与 §4 的「三本」改成「七本 + SOE(+ wdev 取证行)」。

主机单测 44 套 → 47 套(条 2,两把机器尺互证)
- `scripts/check_test_coverage.py --list` 打印 47 行;`ls -d test/host/*/` = 48 个
  一级目录,唯一没有 `run_unit_test.py` 的是 `common/` ⇒ 47 套。
- 差额三名 = 屏板链路套件:`sb_proto`、`sb_link`(2026-10-29 入库)、
  `sb_led_map`(2026-10-30 入库)。专属钩子仍是 22 根 ⇒ 无专属钩子的从 22 套变 25 套。
- 同步面:AGENTS、`docs/门禁说明/README.md`、`host-unit-tests.md` §6 台账(名单补
  三名)、`docs/代码规范/pre-commit使用说明.md`、`主机单测覆盖率口径.md`(含耗时句)、
  `scripts/README.md`、总览目录树、`.pre-commit-config.yaml` 三处注释、
  `test/host/sys_log_mcu/sys_log_mcu_test.c` 一处注释。
- 带日期戳的历史测量值按纪律冻结不动:28 套、41 套 ~105 s、40 套/18 套那三处。

本仓 LVGL 版本(条 6)
- vendored 版本 = `Lib/lvgl/lv_version.h:9-11` 的 9.3.0;总览两处「LVGL v9.2」与
  `gen_arch_svg.py` 图内文字改 v9.3 并重跑产物(脚本与产物同一笔,`generated-fresh`
  逐字节核)。
- **刻意不动**:`docs/内存管理/RAM使用分析与优化方案.md` 两处「LVGL v9.2 文档」+
  GitHub `release/v9.2` 分支 URL ——那是对外游某分支文档的引用,不是本仓版本声明。

触发口径与栈值(条 4、7、9、10)
- ADC 映射 §1「ADC1 为软件触发」是旧口径:`Src/adc.c:53` 实为
  `ADC_EXTERNALTRIGCONV_T3_TRGO` → 改成 TIM3 TRGO,并补一句「TIM3 与 TPAFE 的
  CONVST 之间没有硬件耦合,仍不构成共同采样触发」,缺陷结论本身不变。
- 同文档 adcTask 栈 512 → 640 words(`Src/freertos.c:159 .stack_size = 640 * 4`)。
- `docs/UI设计/UI_设计架构.md` 的 uiTask 栈「16384 words」是单位错:
  实为 4096 words = 16384 B(`Src/freertos.c:218`),顺手补上行号。
- `App/app/app_main.c:197` 注释「DE = PD0」陈旧:`:198` 实传 `ST_DIR4_*`
  (V0.1 实板 PA15,V0.0 才是 PD0)→ 注释改指 V0.1 网表报告的引脚迁移表。
- 两处已退役编号 `N-18`(ADC 映射、V0.1 网表报告)换成登记簿条目名
  「双转换器无共同采样触发」。

核实后确认「文档没问题」的三条(条 8、12、13)
- 黄金参考「菜单 8 状态 + 内容页 66 张」:`simulator/tests/ref/` 实测 66 个
  `shot_*.bin`,与 `ui-golden` 钩子显示名一致 → 不改。
- `App/task/rs485_task.c` 存在但 `Src/freertos.c` 不创建它(grep 无 rs485):
  AGENTS 与总览都已写明「尚未接线 / 暂缓」,图侧画成「实现待接线」→ 不改。
- 104 端口 `:2404` 是工厂默认、可经 `/config.ini` `[comm]` 改 → 总览索引表补
  「默认」二字;「uiTask 10 ms 节拍」核实 = `ui_tokens.h:65 UI_TASK_TICK_MS 10` ✓。

并行勘查会话的同族发现(它扫「文档声明数 vs 真值」,逐处独立复验后采纳 3 处)
- `App/protocol/iec104/iec104_link.h` 头注释写「端口:TCP 2404(§1.5 B0 定义)」,
  同文件下方已写「服务端口真源已迁 [comm] 段」= 自相矛盾 → 补「工厂默认」并指回
  [comm] 与「下次重 bind 即生效」。
- `scripts/gen_task_table.py` 的 iec104Task 描述「:2404 监听」→「工厂默认 :2404
  监听」,`--write` 重生成 README 任务表(AGENTS 表无描述列,实测未被改写)。
- `App/log/README.md` 五行写入点行号全漂:事故 181→188、告警 184→191、启动
  176→183(`App/task/protect_task.c`);操作 `iec104_pts.c` 574/602→582/610、
  `default_task.c` 242→266、`sys_log_mcu.c` 646→682;遥控 573/576/582/586/596/
  598/609 → 581/584/590/594/604/606/617(宿主 `sim_syslog.c:245` 原值正确、未动)。
- **撤销一条**:我早先记的「`docs/算法/继电保护功能复现规划.md:225` 声称记录本已
  有消费者」与原文不符——:225 实为规划表里「动作/SOE 记录 | 复用 `sys_log`」一行,
  属能力复用口径、不是当前态断言 → 不改。教训:**改引用前逐处读现文,别照自己
  上一轮的笔记盲替**(这条比改中的每一处都值钱)。
- 同一批勘查还报了约 12 处「`2404` 缺『工厂默认』限定词」(AGENTS、`App/protocol/
  README.md`、`Src/freertos.c` 注释、5 篇 docs、`test/host` 工具,含一张配图生成器
  要连产物同批重跑)与 `测试开发标准流程.md`「像素层 60 内容页」、
  `.pre-commit-config.yaml` uinav 注释「快检档 18 张黄金参考」(后者写时为真、
  描述的却是钩子现有行为):**本笔刻意不做**,另开任务空间处理——那批要同时动
  AGENTS.md 与 `.pre-commit-config.yaml` 两处单写者面,与并行会话撞车的代价更高。
- 端口生效语义(用户裁定 2026-10-30):产品基线写「公共地址与端口可配…改后需重启
  生效」,代码 `App/task/iec104_serv.c` 是「ca 改后要重启;port 下次重 bind 才生效」
  ——裁定**按代码语义要改**处理(改了端口应立刻重 bind),属固件行为变更,另开任务
  且需真机验证;`.serialtest/config.json` 写死 `i104: listening on 2404` 必检锚点
  是同题(现场改端口会让串口回归假失败),一并留给那条任务。

自查(任务树内)
- `check_doc_links.py --sections-strict`:12 个改动 .md 逐个 rc=0;
  `check_mermaid.py` 改动 .md 全 rc=0;`check_generated_fresh.py` PASS
  (25 个生成器 / 21 一致 / 4 存量基线);`check_nested_comment.py --all` PASS
  (449 文件,新发 0、存量 2);`check_bom_drift.py --all` 99 个名单文件无漂移。
- 编译:`.dsh/skills/keil-build` 退出码 0(0 错误 0 警告),
  Program Size Code=246720 RO=29836 RW=2052 ZI=149708 —— 与本树凭证同码,
  证明这批改的确实只有注释与文档。
- 字节体检:23 个文件无 CR / U+FFFD / 行尾空格 / 缺末行换行;`ui_task.c` 被编辑后
  丢的 3 字节 BOM 已补回(bom-guard 复验通过);5 处「三连空行」逐文件与
  HEAD 比对确认是存量(Python 生成器的 PEP8 双空行与个别 C 文件原有空行)。
- 一次性工具 `ci-artifacts/fix_docs2.py`:断言式规则表,按文件读一次、在演化文本上
  逐条替换、一次写盘。第一版写成「每条规则各自从启动时快照起算」,同一文件后一条
  把前一条整体覆盖,只有每文件最后一条活下来——靠「改完再全仓复扫旧形态」当场抓到,
  这条判据本身已值得写进流程。
- 待办:`git add` 后需重跑 `ram_baseline.py --update`(体积凭证按 git 索引内容生成,
  未 staged 的文件不在凭证里,`check_size_gate.py --local` 现在因此报红)。
- 未 commit、未 push(AGENTS.md 边界:提交属 Ask first)。
2026-10-30 14:19:35 +08:00

41 KiB
Raw Blame History

STM32F4-Base

工业级嵌入式 DTU 平台固件:STM32F407ZGTx @168MHz,集成以太网(CH395F)、SPI NAND 存储(GD5F)、电气量采集(TPAFE5160 + 片内 ADC)、RS-485、FTP 文件服务,自带远程显示(UI 推流)、墙钟对时、看门狗自恢复、运行/自检记录——既是可独立部署的现场设备,也是可复用的平台底座(保护测控等后续产品在此底座上补齐)。产品基线 → docs/产品需求/产品需求设计文档.md §2.1。

仅 Keil MDK-ARM v5 (ARMCC, C99) 工具链;FreeRTOS V10.3.1 / CMSIS-RTOS2;192KB SRAM / 1MB Flash。

任何代码修改后必须先 /keil-build 验证(0 错误 0 警告)再提交;硬件闭环用 /serial-boot-test。

本文件 = agent 入口:宏观概况 + 每次都在场的铁律在这里,细节在 docs/ 与 skills(文末索引)。同一事实只在一处详述(docs/文档规范.md 同此要求)。

项目概况

现状一句话:平台能力(网络 / 存储 / 时间 / UI / 日志 / 看门狗 / FTP / 配置)已实现并台架验收;采集主线已闭环到业务消费者(语义取数与有效性→全周傅氏相量→测频与遥测快照→保护判据 protectTask→104 遥信/遥测+UI,B5/批 2);⚠ 保护判据现处于「未标定闭锁」态——产品构建下 adc_cal_flags() 的 ADC_CAL_F_VALID 未置位,App/task/protect_task.c:268 判停(判定本体 = adc_cal_prot_blocked(),本任务只是消费者),等两点标定写入 [adc_cal] valid 后自动解卡(用户裁定 2026-10-25:保持闭锁,不用万用表走捷径);RS-485 采集尚未接线(B2 按用户决策冻结,见 docs/任务规划/暂缓-RS485.md)——当前最大功能缺口,接线节奏受 RS-485 接线门禁约束(见「平台约束」)。

数据主线(能力视角;分层图与跨层依赖表 → docs/架构设计/项目架构总览.md):

  • 采集:TPAFE5160(计量级 8 路,并行总线)+ 片内 ADC1(9 路,DMA)→ TIM3 1 kHz 节拍 + 原子快照 → adcTask → 语义取数与有效性校验 → 全周傅氏相量 → 测频与遥测快照(meas 200 ms 双区,104 与 UI 共用同一份上游,批 2)+ 保护判据(protectTask 20 ms 判据节拍,B5)→ 104 遥信/遥测上送
  • 存储:GD5F SPI NAND → dhara FTL → FatFs → /log 运行/自检等七本记录 + SOE、/config.ini 配置、FTP 根目录
  • 网络:CH395F → netTask 串行 socket 栈 → FTP(21) / 对时(8888) / RemoDispBus 远程显示(7003) / IEC 60870-5-104 上行(2404)
  • 人机:LVGL 160×160 单色菜单(uiTask),与 PC 模拟器同一份 UI 源码,远端可看图 + 注键

目录地图(只列顶层——稳定边界;细结构自己 ls,别背路径;仓库根不放一次性产物——提交信息草稿 / 临时改写脚本 / 门禁输出与日志一律进 ci-artifacts/(已忽略,口径见 .gitignore 尾注),根目录只留工程配置与两份生成器输入 ui_menu_tree.txt、ui_cn_chars.txt):

目录 职责 可改性
Src/ Inc/ CubeMX 生成(外设初始化 / 中断向量 / freertos.c 任务表) 只写 USER CODE 区
App/ 应用层:app/ 启动与就绪事件 · task/ 任务编排 · algo/ 采集与算法分层(语义取数与有效性 → 保护判据) · cfg/ 配置引擎 · log/ 日志 · fs/ 卷锁 · sync/ 对时 · util/ 工具 手写
Drivers/BSP/ 手写芯片驱动与服务:TPAFE5160 CH395F NET(socket 栈+lftpd) NETCFG GD5F2GQ5UE SD2506 RS485 UI(LVGL 界面) 手写
Drivers/STM32F4xx_HAL_Driver/ Drivers/CMSIS/ Middlewares/ ST HAL / CMSIS / FreeRTOS 第三方,只读
Lib/ 第三方库:lvgl FatFs dhara + lv_conf.h(MCU 端) 只读(本地补丁须有文档登记,如 LVGL I1 补丁)
MDK-ARM/ Keil 工程(.uvprojx、scatter、下载脚本) 工程配置手改;UV4 抖动不入库
simulator/ LVGL PC 模拟器(CMake + SDL2),与固件同标准受检 手写 → lvgl-simulator skill
test/ board/ 板级套件 · host/ 主机侧单测(提交门禁实跑) 手写
scripts/ tools/ 门禁与报告脚本(check_* / gen_* / ci_local.py)· 配图/字体/引用图生成器 手写
docs/ 全量设计 / 测试 / 规范文档(人类索引 docs/README.md) 手写,doc-links 门禁
.dsh/skills/ .gitea/ .serialtest/ agent skills(子域知识入口)· CI 工作流 · 串口/烧录配置 手写

分层边界:上层只依赖相邻下层(业务 → UI/基础服务 → 中间件 → BSP 驱动 → HAL/RTOS → 硬件,单向);新算法模块进 App/algo/,勿塞 App/task/(任务只做消费/编排);新 .c 必须双端注册(new-source-file skill)。

架构要点

Src/main.c(CubeMX 初始化)→ app_main_init → osKernelStart → 9 任务(osKernelStart 之前全是内核启动前代码:禁调任何内核 API,见「平台约束」)

  • app_main_init(时间子系统优先,使后续日志带时间戳):sd2506_init → sys_clock_init → rs485_init → gd5f2gq5ue_init → tpafe5160_init → adc_input_init(语义取数与有效性层)→ SD2506 诊断 → 启动 TIM3 1 kHz 采样节拍
  • 就绪屏障(App/app/sys_events.h Event Flags,取代魔法延时):在用两位 SYS_EVT_NET_READY / SYS_EVT_STORAGE_READY(消费者 osEventFlagsWait 阻塞等待,就绪位 osFlagsNoClear)+ 预留两位 FTP_READY(有生产者、暂无消费者)/ TIMESYNC_READY(生产者消费者都无)→ 逐位对照表 docs/架构设计/项目启动顺序管理.md §3.2
  • netTask:net_stack_bring_up(配置已由 defaultTask 在屏障前加载,本任务只读 RAM 副本)→ 置 NET_READY → 循环 net_poll + net_process_messages;消费 SYS_EVT_NET_RECONFIG → 按网络代次号重建监听(新 IP 免重启生效)
  • defaultTask:f_mount forced(无卷自动 f_mkfs;挂载后卷健康探针,FAT 区陈旧时自愈重格式化)→ 加载网络配置 → STORAGE_READY → LED 闪烁;同时是 /log 的唯一写者(syslog 七本 run/selftest/accid/alarm/start/oper/yk,定长 64B 记录 + soe.log 事件序列;wdev.log 是变长取证行)与配置引擎属主(netTask/uiTask 不得直接触碰配置引擎)
  • adcTask:纯消费者——每拍 adc_input_poll()(TPAFE 原子快照 + 消费一帧 STM32 ADC1 DMA 双缓冲),只取数不做保护运算;节拍与唤醒同源(TIM3 ISR 内触发 CONVST + 同一更新事件经 TRGO 触发 ADC1,并调 adc_tick_irq_notify() 置位,任务 osThreadFlagsWait 等唤醒)。⚠ ISR 侧只能走 adc_tick_irq_notify()(内含"消费者就绪"门,TIM3 比调度器早 ≈100 ms 启动);手写于 Src/freertos.c 的 USER CODE 区,纳入 IWDG required_mask(TASK_WD_ADC)
  • 采集与算法层(语义取数与有效性/全周傅氏相量/保护判据)落在 App/algo/:adc_input/adc_health = 语义取数与有效性,sample_ring/phasor = 全周傅氏相量,freq = 测频与遥测快照(B8,自适应窗),prot_set/prot_elm = 保护判据。板端消费者 = protectTask(B5,2026-10-12):20 ms 节拍 prot_elm_run() + 自检失败闭锁(采样故障闭锁)+ 定值校验和巡检;跨上下文复归(104 YK11)经 prot_elm_request_reset() 落请求。纳入 IWDG required_mask(TASK_WD_PROTECT,产品 0x1F7)。→ docs/算法/采集与算法层设计说明.md(设计与判据)、App/algo/README.md(目录索引)
  • 全景 → docs/架构设计/项目架构总览.md;启动时序与内核前模式 → docs/架构设计/项目启动顺序管理.md
Task 栈 (words) 优先级
defaultTask 1280 Normal
netTask 960 Normal
ftpTask 1024 Normal
testTask 2048 BelowNormal
uiTask 4096 BelowNormal
timeSyncTask 640 Normal
adcTask 640 AboveNormal
iec104Task 640 Normal
protectTask 512 Normal

(实际值以 Src/freertos.c 为准,本表由 scripts/gen_task_table.py 生成、task-table 钩子校验;testTask 仅在启用测试套件时创建。FreeRTOS 堆已迁 CCM,任务栈不占 SRAM;每次抬栈的实测最坏链与余量台账 → docs/内存管理/RAM使用分析与优化方案.md §2.3(任务栈配置与实测峰值)与 §4.0.6(静态最坏链与判定)、docs/内存管理/内存内容分配策略.md §6(容量台账))

铁律:源码编辑与编码

  • 改代码只用 Edit 工具(精确子串替换 / replaceAll);绝不用 Get-Content/Set-Content/cmd 重写含中文的文件——整文件重写按系统默认编码重解码 → U+FFFD 乱码且不可逆
  • Edit 会静默剥掉文件原有的 UTF-8 BOM:带 BOM 的文件(Drivers/BSP/UI/*、App/task/ui_task.c、App/cfg/cfg_file.c、Lib/lv_conf.h)被 Edit 后丢 BOM → ARMCC 改按 CP936 解码 → 中文字面量报 #870-D: invalid multibyte character sequence(只有重编译该文件才暴露,历史"0 警告"不代表安全)。改完须核对 BOM 并按原样补回(只前缀 3 字节 EF BB BF,勿整文件重写)。已机械化:bom-guard 钩子(scripts/check_bom_drift.py:HEAD 有 BOM 而工作区没有即拦;上面这份名单与钩子 files 正则、脚本内 BOM_LIST 三处互指,脚本每次跑都核一次,单独改一处会在别人的提交里被拦下)
  • 字符串字面量必须纯 ASCII:无 BOM 文件里奇数个汉字会吞掉结束引号(偶数侥幸通过)→ 中文只写注释
  • 注释里写作用域/路径别写星号形态:Drivers/BSP/UI/** 里的 /* 会被 ARMCC 当第二次注释起点(诊断 9-D),C 块注释不嵌套;且每个包含者各刷一行、只在重编译那个 TU 时才暴露。写全目录(Drivers/BSP/UI/)或用尖括号(<Drivers/BSP/UI/ui_x.c>)——已机械化 nested-comment 钩子,存量 2 处基线只减不增 → docs/门禁说明/nested-comment.md
  • 怀疑乱码先统计 EF BF BD 字节数(勿信终端/Read 渲染);不要急著 git checkout(会丢弃全部未提交工作)——用 Edit 只修坏行
  • 行尾由 .gitattributes 写死(* text=auto eol=lf;*.bat/*.cmd 强制 CRLF):不依赖 core.autocrlf、不要把工作区文件改回 CRLF → 权威口径 docs/文档规范.md §2
  • 代码规范 V1.0:小写下划线命名(g_/s_/p_/a_ 前缀)、头文件保护宏 XXX_H(禁 __ 前缀)、常量在右、文件头模块注释块 → docs/代码规范/
  • 手改 .dsh/skills/**/SKILL.md 有 65536 字节写侧硬上限(dsh-mnemon 插件 validateSkillContent() 只在写侧校验,加载侧不查):超限的技能照常 skill() 可读、照常进技能目录,但自进化每次「生成草案」都在落盘那一步被拒 → 建议永久卡在「生成中」、界面只剩转圈、连「忽略」都点不动(实测 2026-10-29 两个技能同日各卡一次,第二次的文件 72,601 字节)。加内容前先看体积:node "$HOME/.dsh/scripts/mnemon-skill-ledger-doctor.mjs" --root .(只报不改);正文控制在 ~48 KB(≥75% doctor 就报 CLOSE)。新增的深规程写进同目录 reference/<篇名>.md,正文只留可执行判据的摘要 + 指针,且不重排既有步骤号。已卡死的取证与自愈 → mnemon-skill-ledger-stuck-repair skill。已机械化:skill-size 钩子(scripts/check_skill_size.py,纯标准库;--selftest 五形态、--root 可指到用户级 ~/.dsh/skills 手工量)

构建与验证

  • 门禁时机(用户裁定 2026-10-13):/keil-build / scripts/ci_local.py / check_doc_links.py 只在准备提交时跑(用户让 commit 后统一跑一遍),不是每次中间改动后都跑——多步改动期间直接改即可,验证集中在提交门禁;/serial-boot-test 硬件闭环仍按任务需要跑
  • /keil-build / /serial-boot-test / /task-worktree 是 DSH 会话的技能调用名、不是 shell 命令(bash 里直接敲是 command not found / syntax error)——下面 = 后的命令行才是可执行形态(PowerShell 形态,bash 下路径正斜杠)
  • 编译(/keil-build)= & '.venv\Scripts\python.exe' .dsh\skills\keil-build\scripts\keil_build.py(仓库根执行)。退出码 0=PASS / 1=构建失败 / 2=环境错误(UV4/工程/档案)/ 3=用法 / 4=build 锁被占用;日志 MDK-ARM/build_log.txt;换机必设 DTU_KEIL_UV4 → 细节 keil-build skill + docs/环境与部署/本地工具路径环境变量方案.md
  • 串口回归(/serial-boot-test)= & '.venv\Scripts\python.exe' .dsh\skills\serial-boot-test\scripts\run_boot_test.py:开串口+清日志 → 烧录(固件身份戳保证烧进去的就是这份构建、回读校验)→ wait-idle → 采日志 → 标记校验(含启动锚点计数)。退出码 0=PASS / 1=FAIL / 2=采集或烧录错误 / 3=用法;戳提取规则、锚点计数判据与按模块定制标记见 docs/测试说明/流程与规范/自动化测试流程.md
  • Forbidden 标记含 [ERR]:四个 Fault 的崩溃日志都以 [ERR] 开头 → 出现 Fault 即本次不通过(刻意)
  • 烧录必须 -Rst -Run(stlink-flash skill 的 flash):裸 -P 会报 Programming Complete,但目标核处于死循环/异常时写入并不落地("反复重下载、芯片一直是旧镜像、二分法失效"的根因)。烧后必须回读校验(stlink_flash.py cmp = -CmpFile,No difference found 才算真正写入);⚠ -CmpFile 连接时会复位目标 → 必须排在采日志之后 → docs/测试说明/测试工具/ST-LINK_CLI_复位使用说明.md
  • ⚠️ 旧绝对路径 D:\Code\DTU 程序\... 出现即失效(仓库已搬迁),一律改用仓库相对路径

Python 环境

  • 统一入口 & '.venv\Scripts\python.exe'(3.12,fontTools/Pillow 已装);PATH 上的 python 是 msys64 的,不要用它跑依赖第三方库的脚本;纯标准库脚本任意 python3 均可
  • ⚠ 钩子 entry 的裸 python 不等于 .venv:2026-10-26 真实提交实测 pre-commit 把它解析成机器级解释器(无 fontTools)→ 字体链生成器报 No module named fontTools、generated-fresh 假红(同族第二次发作见 scripts/ci_local.py 文件头 2026-10-11「静态门禁全灭」)。凡依赖第三方库的门禁脚本,一律在脚本内自己按「显式 var > 当前解释器 > 仓库 .venv > PATH」解析并探针校验(装了 pre-commit ≠ 装了 fontTools),但别把 entry 写死成某台机器的绝对路径(那等于把门禁绑死在一台机器上)→ docs/门禁说明/generated-fresh.md §4
  • 重建/换机:<本机任一 python ≥3.10> -m venv .venv + pip install -r requirements.txt(含 pre-commit;DTU_PRECOMMIT_PYTHON 可指向别的装了 pre-commit 的解释器);requirements.txt 必须纯 ASCII(Windows 下 pip 按 GBK 代码页解码)

提交门禁(pre-commit)

  • 任一失败即拦截,共 52 个钩子(口径 = YAML 解析后的 hook 条目数:自查命令与「声明 / 文档 / 实跑」三数同源核对见 docs/门禁说明/README.md §2 首条——⚠ 直接 grep 配置里的声明行会多算一条注释掉的候选钩子(比口径多 1),不是口径;配置注释里也别复述那个 grep 字面量,它会让文本计数再虚高 1(2026-10-27 实测踩过);加一条钩子就同批改 AGENTS 与门禁索引两处计数。2026-10-25 道E 连加 bom-guard / setview-check / uinav-selftest 三条,2026-10-26 加 generated-fresh 一条,2026-10-27 加 nested-comment 一条;2026-10-28 加 id-check-new 一条并同批退役 id-check(口径 52 → 51:编号门禁换牙——新的只查新增行、零语料,旧的八项全库校验连语料一起移入 scripts/archive/id_gate/,手工普查与恢复机器面见 docs/编号规范.md §8.5);2026-10-29 加 skill-size 一条(51 → 52),同日按用户裁定退役 id-check-new(52 → 51:只查新增行的编号门禁整体退场,脚本与白名单一起移入 scripts/archive/id_gate/,编号形态此后无机器面,留档 docs/门禁说明/id-check-new.md);2026-10-30 加 mermaid-syntax 一条(51 → 52:mermaid 围栏内确定性语法——坏图此前只能靠肉眼在渲染端发现,实证见专篇);每种门禁一篇详解(查什么 / 拦下过什么真事 / 限制与盲区 / 基线口径)→ docs/门禁说明/README.md 索引;通用机制与整体口径 → docs/代码规范/pre-commit使用说明.md):通用 5 + gitlint + cppcheck / test-cppcheck + no-mojibake + coding-standard + 文档三件套 doc-links / task-table / mem-numbers + lv-conf-sync(两份 lv_conf.h 特性子集一致)+ cubemx-survival + bom-guard / setview-check / uinav-selftest(2026-10-25 道E 那三条:前半句的加钩流水里有它们、逐项枚举里漏了,补上后本行各项相加才等于 51;2026-10-30 又加 mermaid-syntax 后此句的"等于"目标数为 52)+ 主机侧单测 22 根专属钩子(-unit-test 后缀 = 有独立 files: 触发面、需要秒级归因的那个子集;2026-10-25 增补 adc-cal / prot-dim / vol-idle-sync / ui-data / meas / run-par 六根)——这是口径不是缺口:test/host/ 实测 47 套带 run_unit_test.py(48 个一级目录除 common),其余 25 套(含 test/host/ui_sink、录波三套与屏板链路三套)没有专属钩子,但每次 CI / scripts/ci_local.py 都被 test-coverage 总闸全量真跑并守基线回退,失败照样拦,只是归因落在总闸输出上;没有专属钩子 ≠ 没被跑,22 套台账与补钩子的判据 → docs/门禁说明/host-unit-tests.md §6 第 4 条。Lead 裁定 2026-10-27:不补第 23 根——读「这套跑没跑」去看总闸输出,别数钩子(原写「23 个 / 含 ui-sink 七个」是双重错:既把 22 说成 23,又宣称 ui-sink 有钩子——配置历史里 ui-sink-unit-test 从未存在)+ test-coverage(覆盖率总闸:跑全部 47 套并守基线回退;缺 mscoverage 组件时按用户裁定 FAIL、不 skip,2026-10-24)+ test-suite-switch + registration-check(双端注册反向校验 + UI 页面描述符表接线,登记簿条目「rs485_task 白名单缺口」)+ generated-fresh(生成器幂等性:把 tools/menu_gen/gen_menu.py、中文字库链、tools/arch_diagram/gen_*.py 拷进系统临时目录重跑,与工作区逐字节必须一致——钩子只读、不写仓库也不 stash 别人在飞的改动;实证 0f7b5a87 改了模板文字没重跑生成物,接线前实测另有 5 张 docs/images/ 配图已漂 → 钉成只减不增基线,业务生成物零豁免面 → docs/门禁说明/generated-fresh.md)+ nested-comment(块注释嵌套:注释体内再出现 /* 会被 ARMCC 报 9-D,且按包含者数量刷屏、只在重编译该 TU 时才暴露;本仓诱因就是「在注释里写作用域/通配路径」,改法是写全目录或用尖括号 <Drivers/BSP/UI/ui_x.c>;接线当日实扫 403 个 .c/.h,新发 0、存量 2 处(test/host/common/ 两份假件头)钉只减不增基线 → docs/门禁说明/nested-comment.md)+ skill-size(SKILL.md 不超插件写侧上限 65536 字节:dsh-mnemon 只在写侧校验、加载侧不查,超限技能照常可读却每次「生成草案」被拒、永久卡在「生成中」;另查 reference/ 指针悬空与 BOM → docs/门禁说明/skill-size.md)+ mermaid-syntax(mermaid 围栏语法预检:```mermaid 块里只查「错了必渲染失败」三条——图类型头 / -. 后直接跟管道(本坑:屏板链路设计说明 §1 坏图靠肉眼在渲染端发现,期间各文档门禁全绿)/ subgraph 与 sequence 块的 end 计数配对;零语料空基线、docs/芯片手册/ 与 doc-links 同排除面 → docs/门禁说明/mermaid-syntax.md)+ mem-budget(RAM 92% 预算门禁,登记簿条目「缩堆迁 CCM 后半(暂缓)」)+ font-charset(UI 字符集差集检查,防缺字白框)+ ui-golden(UI 像素回归,菜单 8 状态 + 内容页 66 张黄金参考)+ layer-deps(分层方向:Drivers/BSP/** 不得 include App/** 的头;存量 3 条基线只减不增(2026-10-25 记录 reader 注入削掉 UI 记录 3 条边,6→3))+ bsp-err(BSP 错误码与超时命名约定:公开 API 不得拿 HAL_StatusTypeDef/bool 当错误通道、*_TIMEOUT 宏不得取负值、非 void 须写「返回值:」、返回错误码的函数不得裸丢弃返回值(须接住或显式 (void);实测编译器与 cppcheck 都不报这类,只有这条会响);约定正文 Drivers/BSP/bsp_err.h,存量基线只减不增(2026-10-23 首清点 19 条 → 已还 18;consume-result 上线又暴露 37 条存量丢弃 → 现 38 条))。提交期实跑 49 根(2026-10-30 起,前值是 48;三根慢钩子让位的裁定 2026-10-28):cppcheck / test-cppcheck / test-coverage 三根带 stages: [manual]——实测合计 ~165 s = 一次带代码提交耗时的三分之二,提交期只跑快的那些;这三根由远端 CI 强制(.gitea/workflows/ci.yml 的「手动阶段门禁」步,逐根 --hook-stage manual)与 scripts/ci_local.py 的 manual 段,本地手工复跑 python -m pre_commit run --all-files --hook-stage manual <hook-id>(一次只能一个 id)
  • 范围 = 项目代码:第三方(Lib/、Middlewares/、CMSIS、HAL、lftpd、dhara、MDK-ARM)排除;cppcheck/coding-standard 另排除 test/,规范检查再排除 CubeMX 生成的 Src//Inc/。simulator/ 已纳入全部钩子(2026-10-11)——生成物只靠 .gitignore 挡,不在 exclude 里写目录名(教训见 docs/代码规范/pre-commit使用说明.md §5.5)
  • ⚠ pre-commit 4.x 已移除 always: true(实测对 local 钩子只告警 Unexpected key(s) 并跳过)→ 想"总是跑"的钩子必须让 files: 覆盖到所有会触发它的文件,没有兜底开关
  • 改写型钩子(trailing-whitespace + end-of-file-fixer)豁免 CubeMX/Keil 生成文件(配置里 YAML 锚点 &cubemx-generated);⚠ 只读检查不受影响:cppcheck / 乱码 / cubemx-survival / task-table / mem-numbers 仍覆盖 Src/、Inc/
  • 提交信息:标题 ≤100 字符、正文每行 ≤80(.gitlint + gitlint 默认)→ 长说明一律用短标题 + git commit -F <UTF-8 无 BOM 文件> 多行正文(中文长标题极易超限,且 -m 会被终端代码页转码破坏)
  • CI(.gitea/workflows/ci.yml)只跑静态门禁 + 主机侧纯逻辑单测,刻意不跑 /keil-build(需 Keil 授权)、不含采集链分层(需真机)。远程 CI 的全部资料(服务端开关 / runner 部署与保活 / workflow / 排障 / 实测坑)集中在 docs/CI/README.md,不放本节——它只跑在本机一台 runner win-stm32-ci 上,其他机器与项目都不涉及。日常改代码的等价自查是 & '.venv\Scripts\python.exe' scripts/ci_local.py(静态门禁 + 提交信息门禁 + 主机侧纯逻辑单测)——runner 不在线也不影响本地门禁,不能因"已配 CI"放宽本地要求

平台约束

CubeMX 与 Fault

  • Inc//Src/ 是 CubeMX 生成文件(带 USER CODE BEGIN/END 标记):自定义代码只写标记区之间;Drivers/BSP/ 为纯手工代码
  • 重生成会覆盖标记区之外的一切(.ioc 的 KeepUserCode=true 只保护 USER CODE 区;实测两次重生成各覆盖 4~5 处定制,其中两处不报编译错、只在真出故障时暴露)。防丢失按三层优先级 写进 .ioc → 搬进 USER CODE 区 → 审计兜底;每次重生成后立刻跑 python scripts/check_cubemx_survival.py(已挂 cubemx-survival 钩子)。方法论与全部实测坑(自造标记区会被模板换代抹掉、宏改名 #define f f_Gen 让位、FreeRTOS 钩子必须写进 USER CODE BEGIN 4/5 区…)→ docs/子系统设计/看门狗设计说明.md §10
  • ARMCC __asm 只有一种合法形态:整函数 __asm void f(void) { ... };GCC 内联语法报 #18、块内联把寄存器名当 C 标识符报 #20、取不到 C 的数据符号(A1516E: Bad symbol)→ 三条坑与可用写法 docs/子系统设计/看门狗设计说明.md §10.4
  • 4 个 Cortex-M4 Fault 全部采集现场(手工 __asm Handler 取 MSP/PSP → B 到 per-fault C 入口 fault_entry_{hard,mem,bus,usage} → hard_fault_report 打 [ERR] <TAG> … 后 NVIC_SystemReset());MemManage/BusFault/UsageFault 默认禁用,不使能就被升级成 HardFault、日志看不出根因 → fault_handler_enable() 必须在 app_main_init() 第一行;SHCSR 的使能位无法在 CubeMX 配置(模板只生成优先级)→ 看门狗设计说明 §10

RTOS 与内存

  • osKernelStart 之前禁止任何 FreeRTOS/CMSIS-RTOS 创建类 API(osMessageQueueNew 等会杀死启动,2026-09-20 实证根因)→ 内核前标准模式(纯寄存器/HAL 轮询 + 无锁静态缓冲 + 内核启动后惰性创建真队列)见 docs/架构设计/项目启动顺序管理.md
  • 就绪屏障位必须 osFlagsNoClear:osEventFlagsWait 默认消费所等位,就绪位被先消费掉 → ftpTask 的 WaitAll 永不成立 → FTP 21 端口静默不监听(无日志无报错)。命令位(SYS_EVT_CFG_SAVE/NET_RECONFIG)保持消费型
  • 自研代码禁用 C 库堆分配(malloc/calloc/realloc/strdup/free):2026-10-23 起由提交门禁机器拦截(coding-standard 的 CODE-RULE-20,唯一豁免 = 平台分配后端的宿主侧实现,见 docs/门禁说明/coding-standard.md §7):MicroLIB 的 C 堆由 startup_stm32f407xx.s 的 Heap_Size EQU 0x200 限定为 512 B,KB 级分配必然返回 NULL,而模拟器(宿主 CRT 堆)完全正常、照不出来 → 只用 pvPortMalloc/vPortFree,或把分配收进平台后端 → docs/内存管理/RAM使用分析与优化方案.md
  • 内存预算紧张:新增任何任务/缓冲/栈前先核算 RAM 余量;改栈或加大局部变量前后各跑一次 scripts/stack_report.py(静态最坏链 + 串口 [STACK] 水位)与 scripts/ram_report.py(.map 静态占用);总量红线已机械化:mem-budget 钩子拦 RW_IRAM1/RW_CCM >92%,子系统配额 / CCM 台账 / 趋势见 docs/内存管理/RAM预算台账.md(每次 /keil-build 后跑 scripts/ram_baseline.py --update)→ docs/内存管理/
  • FreeRTOS V10.3.1(CMSIS-RTOS V2);HAL 时基 = TIM7(非 SysTick),tick = 1ms;NVIC 4 位优先级分组,外设中断优先级 ≥ 5

采集链(ADC / RS-485)

  • ADC 取数只用 tpafe5160_snapshot()(临界区原子快照):旧的 get_buf()/data_ready()/clear_ready() 已从驱动删除(两步之间被抢占会拷回两帧混合数据;s_ready 三写者竞态)。消费者侧一律经 adc_input_poll() / adc_input_raw() 按语义取数(不写死下标)→ docs/子系统设计/ADC采样节拍设计说明.md、App/algo/README.md
  • F4 上 ADC1 的硬件触发源与 DMA 流是硬约束:① ADC1 的外部触发清单没有 TIM6/TIM7 TRGO(F4 上它们只接 DAC)→ 同时驱动两片转换器必须用 TIM3(PSC 83 / ARR 999 → 1 kHz + TRGO_UPDATE);② ADC1 在 DMA2 只挂 Stream0 或 Stream4 → 选 Stream4 才不挤走 SPI1/USART1 RX,且换流会被重置为优先级 0(违反"外设中断 ≥5")→ 改流后必须回 NVIC 核对 Src/dma.c;③ DMAContinuousRequests 必须 ENABLE(关掉 = 一轮 9 通道只发一次请求,静默丢 8 路);④ DMA 目的缓冲必须在 SRAM(DMA2 不可达 CCM)→ docs/硬件平台/ADC通道映射说明.md
  • STM32F4 ADC 上电必须自校准:CubeMX 不生成这一步,本仓裁剪版 HAL 没有 HAL_ADCEx_Calibration_Start、裁剪版 CMSIS 头没有 ADC_CR2_CAL → 按 RM0090 §13.3.6 手写 adc1_calibrate()(Src/adc.c 的 USER CODE BEGIN 1 区)+ MX_ADC1_Init() 末尾调用;缺它不报任何错,只是读数带失调
  • 接 ADC / RS-485 消费者前的强制门禁(RS-485 接线门禁):先确认驱动契约已修——TPAFE 三条 ✅(原子快照取数 / 帧序号取代就绪电平标志 / FRSTDATA 数据有效性校验);RS485 已修两条 ✅,但 单缓冲包撕裂、TX 超时拉 DE 两条未修 ❌ → rs485_task 仍被挡。建任务前后各跑一次 stack_report.py

存储与网络外设

  • FatFs(Lib/FatFs 移植版)两条台架实证陷阱:① 追加写必须 FA_WRITE|FA_OPEN_APPEND——该移植 FA_OPEN_APPEND 不含 FA_WRITE,不带写位打开的文件 f_write 恒返 FR_DENIED;且 f_write 簇分配失败时返回 FR_OK+短写(无错误码)→ 判失败必须校验写回字节数。② FF_USE_LFN=0 → 只支持严格 8.3 短名(不得以点开头),.fschk 恒返 FR_INVALID_NAME;自愈类逻辑严禁把名字/参数类错误码归入卷损坏触发集 → 完整口径与标志位编号表 docs/架构设计/STM32F4-Base存储架构说明.md §10.1
  • CH395F SPI:每次事务须 ch395f_spi_begin()/end() 包裹;分频系数必须为 8(PCB 约束,勿"优化")

日志与工具链

  • DBG_* 每条日志约 90 B Flash + 216 B 栈(每个调用点内联行缓冲 + 时间戳缓冲)→ 日志不是免费的,已覆盖的步骤别加重复锚点,只在原先静默返回的黑洞处补;深调用链模块在 #include "dbg_log.h" 之前 #define DBG_LINE_BUF_MAX 128U(取值必须 ≥ 该模块最长日志行,否则该行被截断)
  • MDK-ARM/*.uvprojx 每次 /keil-build 后被 UV4 自动改写,改动一律不入库(XML 归一化 + PackID 降到本机 DFP 版本);判据与例外(真的增删源文件才提交)→ keil-build skill

硬件引脚

  • 完整引脚表(含 SD2506 I2C1、SWD、调试口)→ docs/架构设计/硬件规格与能力边界.md §2(权威,V0.1 口径;勿在本文重复抄表)
  • ⚠ 网名 AINn ≠ 板级 ADCn ≠ 转换器通道号(17 路里 13 路置换)→ 取数必须查 docs/硬件平台/ADC通道映射说明.md(TPAFE buf[2]=IA/buf[5]=UN/buf[6]=UA;网名 ADC17=PC4 用 ADC_CHANNEL_14,不是 ADC_CHANNEL_12)
  • ⚠ 8 路电气量被分给两片转换器是 V0.1 硬件缺陷,不是有意分配(跨转换器无共同采样触发 → 无同一时刻性、相位不确定)→ V0.1 上禁止跨转换器做相位/功率/功率因数/矢量运算;V0.2 须把 8 路全接 TPAFE(登记簿条目「双转换器无共同采样触发」)

模块约定

  • CH395F 单连接 socket:监听与数据通道复用同一 Socket(CONNECT 后转数据通道,DISCONNECT/TIMEOUT 后重开 → LISTEN);所有 CH395F 中断在 net_poll() 内经 GET_GLOB_INT_STATUS 集中读取并清除,不得在 ISR 或其他地方单独处理。⚠ 全局寄存器整体读即清、芯片无按位清除手段,而 net_poll 只在全局位为 1 时才读该 Socket 的 SINT → "顺手全量清一遍"等于把别的在用 Socket 的门焊死(B3 实证)。现存读清点全量审计 + 新增一处读清的三条准入判据 → docs/网络/网络自愈架构设计.md §4.10
  • 所有 net_* socket 操作经 netMsgQueue 在 netTask 串行执行;非 netTask 任务直接调 net_* API,勿直接碰 CH395F 驱动
  • net_* 公开 API 的错误码经"任务通知值"回传:net_msg_t 是按值拷入队列的,netTask 改的是自己那份副本,调用方永远看不到 → 错误码搭 xTaskNotify() 通知值回来。勿给 net_msg_t 加"回传用"字段(调用方只会读到栈垃圾)
  • 单连接 socket 的断开语义:EOF 时 net 层已 auto_relisten,应用层不得在 EOF 时 net_close 该 fd(会连监听一起毁掉 → 端口"连接被拒");仅当数据 fd ≠ 监听 fd(模拟器多连接后端)时才关
  • 网口配置:/config.ini [network] 段(工厂默认 192.168.1.100/24、gw .1;MAC 全零 = 芯片内置;UI 保存立即生效;旧档 /cfg/netcfg.bin 迁移已于 2026-10-10 删除,旧档存在则被忽略);同文件 [ftp] 段 = FTP 鉴权(user / pwd_hash = SHA-256(user‖password) / write,出厂默认 admin/admin,默认口令在用有启动告警;连续 5 次失败锁 60 s)→ docs/子系统设计/网络配置说明.md
  • 新 .c 文件必须手动注册到两处才能双端编译:MDK-ARM/STM32F407-Demo.uvprojx + simulator/CMakeLists.txt(用 new-source-file skill);⚠ 新文件落在还没有的新目录,还要把该目录加进 uvprojx 的 <IncludePath>——否则报错落在消费者 TU 上(cannot open source input file),极易误判
  • 测试构建:test/board/test_config.h 的 TEST_SUITE_CH395F/STORAGE/GD5F/RTC 互斥(一次只能一个,#error);全部注释 = 产品构建,受 test-suite-switch 钩子门禁;ENABLE_DESTRUCTIVE_TESTS 默认关闭(关闭时套件只读模式,可随时在在用设备上跑)——⚠ 跑一轮破坏性 GD5F 即让 dhara 多记一个坏块且该计数持久化,普通恢复清不掉 → docs/测试说明/专项验证/存储维护与容量恢复.md;板级套件运行用 board-test-suite skill
  • 改驱动前先读对应 docs/问题记录/*_Trap_Records.md(CH395F / GD5F2GQ5UE / SD2506);各模块测试指南见 docs/测试说明/
  • 改 UI / 绘制缓冲前读两条实证坑(I1 palette 头越界、白底白字整屏纯白)→ docs/UI设计/UI_设计架构.md §1.4 第 8/9 条,ui-verify skill 亦载

文档与图形

  • 登记簿类文档「完成即销项」:docs/任务规划/README.md 这类待办登记簿,事项修复/关闭后直接从表中删除该行(不保留"已消化"行)——结论与证据应已沉淀进对应模块文档/问题记录。销项时必须:① 先核对目标文档确已含完整记录 ② 同步清理其他文档对该编号的引用 ③ 改完跑 check_doc_links.py --all + pre-commit。新条目标识(用户裁定 2026-10-14;2026-10-29 收口为一律不再发号):# 列填语义短语(如 沉降窗 800ms 上限只留痕),不再新增字母+数字编号——"编号不回收"只写规则却不留痕迹就不可执行(销项删行后仓库里毫无痕迹,2026-10-14 实测就发生过「销项号被重新取走」的撞号事故),语义标识自带唯一性;存量编号(登记簿 N-x / S-x / T-x 与专项 A-x)已于 2026-10-26 全部转成语义短语,旧号进 scripts/archive/id_gate/id_aliases.json 标 retired-to-phrase、不得再指;「数据可信」任务卡号(中文前缀「卡」+ 数字,14 个)已于 2026-10-27 随卡销项整族清退,换算表 scripts/archive/id_gate/编号治理历史.md §9.3——这一族刻意不进 id_aliases.json:中文前缀不进 check_ids 语料,登记了机器也看不见,没有机器识别面,停发只靠规范与评审;已随销项消失的号永久作废、不得复用 → docs/任务规划/README.md §0
  • 图形走 draw-diagram skill:精确布局图用 tools/arch_diagram/gen_*.py 生成器(产物放 docs/images/,脚本与产物同一笔提交:改生成器没重跑,下一个碰巧重跑的人会看到一段「不是我改的」diff——已机械化 generated-fresh,重跑到临时目录逐字节比对 → docs/文档规范.md §4.4);拓扑图用 Mermaid;勿手绘 ASCII 框图。文件引用关系图走 tools/refgraph(& 'tools\refgraph\gen_refgraph.bat')
  • 改名 / 挪目录一律走 scripts/rename_ref.py(先 dry-run,确认后 --apply):git mv + 自动改写全库引用(反引号引用 / Markdown 链接 / 图片 ../ 层级换算 / #include / uvprojx <FilePath> / CMakeLists.txt),落盘后自动跑 check_doc_links --all 复核——手工扫引用既慢又漏(一次改名历史上波及 10~30 文件)。引用章节用「路径.md §N.M」紧邻写法(doc-links 校验编号是否还在:紧邻/裸名/前缀/别名/自引用全部拦提交(钩子带 --sections-strict);每次跑打印「可判定比例」,--section-todo 出待改清单,昵称歧义就登记 scripts/section_aliases.json 或写清楚)→ docs/文档规范.md §3.5 / §3.6。编号改名走 scripts/rename_ids.py(映射表驱动、显式 scope、干跑默认、自动写 scripts/archive/id_gate/id_aliases.json 的 scoped 旧号→新号条目;[示例]/[计划] 行与变更记录表不改写;绝不全局查找替换——同一裸形字面量可能同时属多个族,全局替换等于造新撞号,实测 S3 三义)
  • 文档写作规范(编码/BOM/链接/表格/一致性钩子)→ docs/文档规范.md;全量索引与推荐阅读顺序 → docs/README.md

边界(Ask first / Never)

Ask first(须用户明确指示才做,均不是任何任务的隐含步骤):git commit / git push(构建与测试通过后停下等指令)· 开 ENABLE_DESTRUCTIVE_TESTS 跑破坏性套件(持久代价 → 模块约定)· CubeMX 重生成(→ cubemx-regen skill)· 登记簿删行销项(→ 文档与图形)· 建任务/抬栈/加大缓冲(先跑预算脚本 → 平台约束)

Never(任何任务):用 Get-Content/Set-Content/cmd 重写含中文文件、用 git checkout/reset --hard 丢弃未提交工作(→ 铁律)· 在 USER CODE 区之外写定制代码、自研代码写 C 库堆分配(→ 平台约束)· 非 defaultTask 直接触碰配置引擎、非 netTask 直接碰 CH395F 驱动(→ 模块约定)· 提交 UV4 抖动的 *.uvprojx、提交 .mnemon/(记忆工具本地状态,已 .gitignore)

按主题查哪份文档

主题 去处
全量索引 / 推荐阅读顺序 docs/README.md
架构全景 / 启动时序与内核前模式 docs/架构设计/项目架构总览.md、docs/架构设计/项目启动顺序管理.md
产品需求基线(做到什么程度) docs/产品需求/产品需求设计文档.md
看门狗 + CubeMX 重生成存活方法论 + ARMCC __asm docs/子系统设计/看门狗设计说明.md §10
ADC 采样节拍 / 通道映射 / V0.1 硬件缺陷 docs/子系统设计/ADC采样节拍设计说明.md、docs/硬件平台/ADC通道映射说明.md
存储栈(FatFs / dhara / lftpd / 两条 FatFs 陷阱) docs/架构设计/STM32F4-Base存储架构说明.md
RAM / 栈预算 / 容量台账 docs/内存管理/RAM使用分析与优化方案.md、内存内容分配策略.md
每个提交门禁一篇详解(查什么 / 拦下过什么真事 / 限制与盲区 / 基线口径) docs/门禁说明/README.md(52 个钩子 → 30 篇索引,其中 2 篇是退役留档 + 1 指针)
还想给东西发编号?(2026-10-29 用户裁定:一律不发,写语义短语;§4.1 五个存量族的既有号只读引用——「停发」不等于「暂缓」,也不等于「可以清号」)/ 编号门禁现状(两道都已退役:全库八项校验 2026-10-28、只查新增行 2026-10-29;此后无机器面,只靠 §1 与评审) docs/编号规范.md(§1 默认答案、§4.1 存量族〔穷举〕与『已停发』栏、§8.5 手工普查与恢复机器面)
提交门禁通用机制(阶段 / stash 陷阱 / 触发面 / 增量拦截三段式) docs/代码规范/pre-commit使用说明.md §2
代码规范 V1.0 / cppcheck 口径 docs/代码规范/嵌入式C语言代码规范_V1.0.md、cppcheck使用说明.md
自动化测试 / 实机测试 / 板级套件 docs/测试说明/流程与规范/自动化测试流程.md、实机测试实施指南.md、board-test-suite skill
烧录与复位语义 docs/测试说明/测试工具/ST-LINK_CLI_复位使用说明.md
串口助手 serial-assistant skill(命令/判据/陷阱,用到自动加载)、docs/测试说明/测试工具/串口助手使用指南.md、串口助手设计说明.md
UI 架构 / 模拟器 / UI 自动化回归 lvgl-simulator skill(构建/自检/报错速查,用到自动加载)、docs/UI设计/UI_设计架构.md、LVGL模拟器部署与使用说明.md、UI_自动化测试流程.md;UI 改动回归走 ui-verify skill
待办与审计发现登记簿 docs/任务规划/README.md
多任务并行开发(开空间 / 合并回收 / 台架与构建排队);DSH worktree 插件的仓内树怎么接上(.dsh-worktrees/ 缺 .venv、plugin adopt、锁归属 pw:<name>) task-worktree skill(@task 仅为文档别名非可执行命令,显式调用 /task-worktree,用到自动加载;插件桥 → 其 F 节)、docs/环境与部署/并行任务工作树方案.md §3.4
换机 / 工具路径环境变量 docs/环境与部署/
远程 CI(Gitea Actions,只跑本机一台 runner) docs/CI/README.md