diff --git a/.dsh/skills/keil-build/SKILL.md b/.dsh/skills/keil-build/SKILL.md
index 048386a7..a84e07b2 100644
--- a/.dsh/skills/keil-build/SKILL.md
+++ b/.dsh/skills/keil-build/SKILL.md
@@ -35,9 +35,11 @@ whenToUse: 需要编译 Keil MDK 工程、验证代码改动是否 0 错误 0
硬件闭环只会把编译错误伪装成运行问题。
- **警告也算失败**:判 PASS 只认退出码 0,别只看"有没有 error:"。
- **`MDK-ARM/*.uvprojx` 每次构建后被 UV4 自动改写,改动一律不入库**:UV4 会把工程
- XML 归一化成本机风格(`` → `` 等)并把 `PackID` 降到本机 DFP 版本。
+ XML 归一化成本机风格(`` → ``、XML 声明加 `standalone="no"`)并把
+ `PackID` 降到**本机装的 DFP 版本**(属降级——提交后会把别人的 Pack 版本一起改掉)。
判断:`git diff` 里只见 XML 格式/`PackID` 差异而**无源文件挂载增删** →
`git checkout -- ` 丢弃;只有真的增删了源文件(grep 文件名计数变化)才提交。
+ 同目录的 `*.uvoptx` / `*.uvguix.*` 是 IDE 个人设置,已在 `.gitignore` 里,不会露出来。
- 报错定位读日志别只看终端回显:本仓库 = `MDK-ARM\build_log.txt`。
- **exit 2 先分清"真没设"还是"会话没继承"**(2026-10-10 实测):agent 的 shell 由
常驻服务派生,服务启动**之后**设置的用户级环境变量不会自动进来。查
diff --git a/.dsh/skills/lvgl-simulator/SKILL.md b/.dsh/skills/lvgl-simulator/SKILL.md
new file mode 100644
index 00000000..08dfd725
--- /dev/null
+++ b/.dsh/skills/lvgl-simulator/SKILL.md
@@ -0,0 +1,52 @@
+---
+name: lvgl-simulator
+description: LVGL PC 模拟器构建/运行/自检入口:build_sim.bat 自动探测本机 VS、lvgl_sim.exe 无头截图与自检开关(--screenshot/--keys/--standby/--cfgtest/--logtest/--idle)、三套回归脚本、两份 lv_conf.h 同步门禁、构建报错速查(SDL2/lv_conf 找不到、全黑、键盘无响应)。
+whenToUse: 需要构建或运行模拟器、无头截图出图、跑模拟器自检或 UI 回归、修改 simulator/ 目录代码或 simulator/lv_conf.h、排查模拟器构建/运行异常时。UI 改动后的完整回归走 ui-verify skill。
+---
+
+# LVGL PC 模拟器(simulator/)
+
+PC 上编译运行与 MCU **同一份** UI 源码(CMake + MSVC + SDL2,x64,160×160 I1 单色),免烧录。
+**目录架构 / 完整坑与 FAQ → `simulator/README.md`;首次部署(SDL2 SDK)→ `docs/UI设计/LVGL模拟器部署与使用说明.md`。本 skill 只放操作入口。**
+
+## 构建与运行(仓库根执行)
+
+```powershell
+& "simulator\build_sim.bat" # 增量构建;vswhere 自动探测本机 VS(16/17/18),勿写死生成器
+& "simulator\build_sim.bat" -Clean # 全量重建(遇到奇怪问题先加它)
+.\simulator\build\Debug\lvgl_sim.exe # 窗口模式:↑↓←→ / Enter / Esc(纯键盘,无鼠标)
+```
+
+⚠ 窗口模式默认 **60 s 空闲自动退出**(防挂后台),`--idle <秒>` 覆盖。
+
+## 无头自检(退出码 0=PASS / 非 0=FAIL)
+
+| 命令(在 `simulator\build\Debug\` 下或用全路径) | 用途 |
+|---|---|
+| `lvgl_sim.exe --screenshot shot.bin` | 离屏渲染首帧 → `simulator/i1_to_png.py` 转 PNG 目检 |
+| `lvgl_sim.exe --keys "ud" shot.bin` | 注入键序后截图:`u`=↑ `d`=↓ `l`=← `r`=→ `e`=Enter `s`=Esc(`main.c` 实锤) |
+| `lvgl_sim.exe --standby shot.bin` | 待机页截图 |
+| `lvgl_sim.exe --cfgtest` | 配置引擎闭环:加载→轮询→外部改文件重载→外科式保存回验(在当前 CWD 产生 `sim_config.ini`) |
+| `lvgl_sim.exe --logtest` | syslog 后端闭环(定长记录/时间戳/轮转) |
+
+## UI 改动回归 → 用 `ui-verify` skill(勿在此零散手跑)
+
+5 步门禁 = 构建 → `check_menu_rows.py`(菜单 8 状态像素 + 池基线)→ `check_content_pages.py`(文本断言 + 11 页黄金参考 + 往返池)→ `@build` → `coding-standard`。
+RemoDispBus 推流回归单独跑:`.venv\Scripts\python.exe simulator/tests/check_remo_sink.py`。
+黄金参考 `tests/ref/` 是**本地生成物不入库**:换机后先 `check_content_pages.py --update` 生成,再 `regen_ref_png.py` 出目检 PNG。
+
+## 两条铁律
+
+- **改 `simulator/lv_conf.h` 触发 `lv-conf-sync` 门禁**:它与 MCU 端 `Lib/lv_conf.h` 必须保持同一特性子集(按**生效值**比对,未定义取 `lv_conf_internal.h` 兜底);白名单与四类判定 → `docs/代码规范/pre-commit使用说明.md` §2.15 / §5.5
+- **模拟器代码与固件同标准受检**(`cppcheck` / `coding-standard` / 乱码 / 改写型钩子 / `doc-links` 全覆盖 `simulator/`);**改动触及 `sim/` 任何后端,必须同步在 `docs/测试说明/模拟器差距清单.md` 登记一行**——模拟器跑过 ≠ 真机跑过
+
+## 构建/运行报错速查(详解 simulator/README.md §4)
+
+| 现象 | 根因 |
+|---|---|
+| 找不到 `SDL2/SDL.h` | LVGL SDL 驱动写死该路径,头文件须有 `include/SDL2/` 副本(仓库已内置) |
+| 找不到 `lv_conf.h` | `LV_CONF_DIR`/`SDL2_INCLUDE_DIR` 必须在 `add_subdirectory(lvgl)` **之前**定义 |
+| 窗口全黑 | mono 主题未生效 / 缺 `lv_refr_now(NULL)` / 重复喂 tick(SDL 驱动内部已 `lv_tick_set_cb`) |
+| 画面正常但按键全无效 | 主循环**不得**调 `SDL_PollEvent`——LVGL SDL 驱动内部 5ms 定时器统一分发,先轮询会抢光事件 |
+| 中文显示空白 | 子集字体缺字 → **`font-regen` skill** 重生成 `ui_font_cn_12` |
+| 新加 UI `.c` 一端编不过 | 双端注册(uvprojx + CMakeLists)→ **`new-source-file` skill** |
diff --git a/.dsh/skills/new-machine-setup/SKILL.md b/.dsh/skills/new-machine-setup/SKILL.md
index b5b73ed0..72f28007 100644
--- a/.dsh/skills/new-machine-setup/SKILL.md
+++ b/.dsh/skills/new-machine-setup/SKILL.md
@@ -46,7 +46,7 @@ whenToUse: 换新机器/新克隆要把环境搭起来、构建或烧录报"环
(`env_check.py` 对这种情况报 WARN 并给注入命令。)
- 换机后 COM 口几乎必变:只改 `.serialtest/config.json` 的 `portName`,
然后 `serial_tool.py restart`(daemon 启动时缓存配置)。
-- 串口"拒绝访问"先查残留旧 daemon(AGENTS.md 串口助手节),不是驱动问题。
+- 串口"拒绝访问"先查残留旧 daemon(`serial-assistant` skill「陷阱」节),不是驱动问题。
## 其它项目怎么套
diff --git a/.dsh/skills/ui-verify/SKILL.md b/.dsh/skills/ui-verify/SKILL.md
index 370e4d2f..10c1b735 100644
--- a/.dsh/skills/ui-verify/SKILL.md
+++ b/.dsh/skills/ui-verify/SKILL.md
@@ -1,13 +1,18 @@
---
name: ui-verify
description: UI 改动后的回归验证:5 步门禁(模拟器构建、菜单像素回归、内容页黄金参考、Keil 构建、代码规范)。
-whenToUse: Drivers/BSP/UI、字体 ui_font_cn_12.c / ui_cn_chars.txt、菜单/内容页被修改之后,或用户要求"验证 UI / 跑 UI 回归 / UI 改动检查"时。
+whenToUse: Drivers/BSP/UI、字体 ui_font_cn_12.c / ui_cn_chars.txt、菜单/内容页被修改的前后(写码前先读「两条实证坑」节),或用户要求"验证 UI / 跑 UI 回归 / UI 改动检查"时。
---
# UI 回归验证
UI 相关改动完成后必须跑完 5 个门禁再提交。完整说明见 `docs/UI设计/UI_自动化测试流程.md`。
+## 两条实证坑(写码前先读,模拟器照不出来)
+
+- **I1 绘制缓冲含 8B palette 头**:布局 `[palette 8B][pixel data]`,但 LVGL 容量校验只看 `stride×h` → 只给 `stride×h` 会把末 8 字节写到缓冲外。`ui_port.c` 按 `stride×h + palette` 供给并在 flush 时 `+palette` → `docs/UI设计/UI_设计架构.md` §1.4 第 8 条
+- **MCU 端必须显式应用 mono 主题**:`lv_theme_mono_init(disp, true, ...)` + `lv_display_set_theme()` + 对活动屏幕再 `lv_theme_apply()`,否则活动屏仍是默认浅灰而内容恒用白色 → **白底白字整屏纯白**(远端"纯白屏"根因)→ `docs/UI设计/UI_设计架构.md` §1.4 第 9 条
+
## 步骤(仓库根目录执行,python 一律用 .venv)
```powershell
diff --git a/AGENTS.md b/AGENTS.md
index c48cedf5..849086e3 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -1,66 +1,53 @@
# STM32F4-Base
-STM32F407ZGTx @168MHz(HSE 8MHz → PLL M=4/N=168/P=2);**仅 Keil MDK-ARM v5 (ARMCC, C99) 工具链**;FreeRTOS V10.3.1 / CMSIS-RTOS2;192KB SRAM / 1MB Flash。
+**工业级嵌入式 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。
**任何代码修改后必须先 `@build` 验证(0 错误 0 警告)再提交;硬件闭环用 `@test`。**
-## 铁律:源码编辑与编码
+> 本文件 = agent 入口:宏观概况 + 每次都在场的铁律在这里,细节在 `docs/` 与 skills(文末索引)。同一事实只在一处详述(`docs/文档规范.md` 同此要求)。
-- 改代码**只用 Edit 工具**(精确子串替换);批量替换用 Edit 的 replaceAll。**绝不用 `Get-Content`/`Set-Content`/`cmd` 重写含中文的文件**——整文件重写会按系统默认编码重解码 → `U+FFFD` 乱码且不可逆
-- **字符串字面量必须纯 ASCII**(2026-09-15 实测):本机 ARMCC 对**无 BOM** 源文件按 CP936 解读,字符串内含**奇数个**汉字时末尾高字节吞掉结束引号 → 编译错(偶数个汉字侥幸通过);中文只写注释(注释不参与解析)。带 BOM 的文件(`Drivers/BSP/UI/*`、`App/task/ui_task.c`、`App/cfg/cfg_file.c`、`Lib/lv_conf.h`)经 BOM 按 UTF-8 解码,不受此条限制
-- 确需脚本级重写:工程源码为 UTF-8(多数文件无 BOM,UI 系带 BOM),按原样同编码回写、保持 BOM 状态;拿不准就别碰
-- 发现乱码:先统计 `EF BF BD` 字节数确认真损坏(勿信终端/Read 渲染);**不要急著 `git checkout`**(会丢弃全部未提交工作)——先用 Edit 只修坏行
-- **Edit 工具会剥掉文件原有的 UTF-8 BOM**(2026-09-18 实测):带 BOM 文件(如 `App/cfg/cfg_file.c`)被 Edit 后 BOM 静默消失 → ARMCC 改按 CP936 解码 → 中文字符串字面量报 `#870-D: invalid multibyte character sequence`(**只有重编译该文件才暴露,历史"0 警告"不代表安全**)。改完带中文字面量的文件须核对 BOM 并按原样补回(只在文件头前缀 3 字节 `EF BB BF`,勿整文件重写)
-- **行尾策略已写死进 `.gitattributes`(2026-10-09)**:`* text=auto eol=lf`(仓库 blob 与工作区统一 LF)+ `*.bat`/`*.cmd` 强制 CRLF(cmd.exe 对 LF-only 的 goto/标签不可靠)+ PNG/TTF/DLL/LIB 等显式 `binary` + `*.jpg` 走 LFS 且 `-text`(该条必须排在 `*` 之后才能覆盖)。**不要再依赖 `core.autocrlf`,也不要把工作区文件"改回 CRLF"**——CubeMX 重生成写的就是 LF;此前每次重生成后 40+ 个第三方文件在 `git status` 里显示"已修改"(内容归一化后一致),根因就是行尾由每台机器各自的 autocrlf 决定。第三方/生成文件的"文件末尾空行"已一次性对齐工具输出(`53bd9af`),**勿再用改写型钩子去修剪**(已在 `trailing-whitespace`/`end-of-file-fixer` 豁免)
+## 项目概况
-## 构建与验证
+**现状一句话**:平台能力(网络 / 存储 / 时间 / UI / 日志 / 看门狗 / FTP / 配置)**已实现并台架验收**;**ADC / RS-485 这两个 DTU 核心采集职责尚未接线到业务消费者**——当前最大功能缺口,接线节奏受 E-59 门禁约束(见「平台约束」)。
-- `@build` = **`keil-build` skill**:`& '.venv\Scripts\python.exe' .dsh\skills\keil-build\scripts\keil_build.py`(仓库根目录执行;纯标准库,任意 python3 亦可)。退出码 0 = 0 错误 0 警告(PASS)、1 = 构建失败(打印报错行)、2 = UV4 未找到/无效、3 = 用法错误。UV4 解析链 `--uv4` > `.serialtest/config.json` 的 `uv4` > `$DSH_KEIL_UV4` > `$DTU_KEIL_UV4` > PATH(**显式设了但无效 = 立即报错、不静默回退**);日志 `MDK-ARM/build_log.txt`。⚠ **原 `MDK-ARM/build.bat` 已于 2026-10-10 删除**(判定口径同款,但无超时保护、UV4 只认 `DTU_KEIL_UV4`、失败时 dump 整份日志);`test/run_all_suites.py` 已改调本 skill。换机必设 `DTU_KEIL_UV4`,见 `docs/环境与部署/本地工具路径环境变量方案.md`
-- `@test` / `.dsh/skills/serial-boot-test/scripts/run_boot_test.py` 一键闭环(2026-09-20 起为纯 Python 标准库实现,替代 PowerShell 版;`& '.venv\Scripts\python.exe' .dsh/skills/serial-boot-test/scripts/run_boot_test.py`):开串口+清日志 → 烧录(**`stlink-flash` skill**:`stlink_flash.py flash` = `-P -Rst -Run`;`flash_cmd` 默认取 `.serialtest/config.json`,本仓库已指向本 skill)→ **固件身份戳**(从刚烧进去的 hex 里抠 `fw build: