chore(工具,文档): 台架 rc 语义与导航硬闸用例、门禁环境诊断、护栏还债算法、mem-budget 跨树口径
四条道的收尾件同笔(全是 python 工具与文档,零固件源;各段归属按道署明)。 · **台架(bench-runner)**:verify_log.py 新增 classify_measurement() 与 measureme nt.* 字段,零行/零锚点 判 exit 2 并打 [MEASUREMENT-BROKEN],**但同帧有 Forbidden 命中或自检故障仍判 1**(专门造了「零锚点 + [ERR] MEMFAULT」的陷阱日志实测——崩溃不许被降级成「没数据」);--selftest 16 → 22 条。board_ui_nav.py 把两条执行页安全闸做成 7 条常驻用例接进提交钩子跑的快检档,作者把自己用例**证伪三轮**(测试强制上限、 清单清空时是崩不是判定、出厂默认被翻允许也照样绿)后三条危险改动逐条能红。台架卫生清单落 §7:每圈复位 前 open + 断言 ready + 看 rx 增长(今晚 daemon 死了 status 自愈把它遮住)、**Forbidden 令牌不得落在 英文单词内部**(裸 Fault 命中 default 两次假红)、改判定语义前先 grep 消费者。 · **环境(ui-param-writer)**:证伪卡片前提两处——identify 那个版本根本没有 tags 模块(RECORD 与磁盘逐项 一致,pre-commit 自己也从 identify.identify 取 ALL_TAGS,全仓零引用),真问题是 44 条 system 钩子里 42 条 entry 用裸 python → PATH 首位那个零第三方包解释器,今天没炸纯靠两件事同时成立。交付是**新增 entry 硬规则 + 修复死顺序与回滚并标注等静默窗口**,没有动共享环境(此刻重装会让别人的钩子在半途换解释器)。 · **门禁(gate-docs-fixer 已入库的 51 号钩子)**:本篇带上 host-unit-tests 的裁定句落点说明。 · **内存(comm-config-writer)**:mem-budget skill 补跨树测量身份口径(脚本按 __file__ 定位产物、什么才算 一次新测量、多链同涨同一数值的共享帧/幻影环归因法)+ 看门狗说明的指针行。 实测:verify_log --selftest 22/22、board_ui_nav --selftest-quick rc=0(硬闸 7/7)、 doc-links --all exit 0、 check_ids --strict exit 0 且绝对数 1,156 **未被本笔吃掉**;全部文件 LF / 无 BOM / U+FFFD=0 ;本笔零固件源, 随上一批权威构建验证过(0 错 0 警),故不再单独 @build。
This commit is contained in:
@@ -493,7 +493,7 @@ snapshot == required → miss_streak=0、证据门闩复位、喂狗
|
||||
|
||||
看门狗是本项目**第一次**把 CubeMX 生成流程引入可靠性关键路径,由此沉淀出与看门狗本身无关的通用方法论。`AGENTS.md`「平台约束」指向本节,改动 `.ioc`/`Src/`/`Inc/` 前请先读。
|
||||
|
||||
> **2026-10-26(道G task-26)一句话登记,机制不在本文复述**:四个 Fault 与栈溢出 / 断言钩子的**崩溃上报发送口已归一**进控制台单写者(`dbg_console_write_raw` + 一次整行独占,不再自行调用 `HAL_UART_Transmit`),机制正文与残余见 `docs/子系统设计/控制台单写者日志设计说明.md` §4.4.1。本节只关心一件事:这些实现仍须整体待在 `Src/freertos.c` 的 `USER CODE BEGIN 4/5` 区与 `Src/stm32f4xx_it.c` 的 `USER CODE BEGIN 0` 区(§10.2 的 L2),改完跑 `scripts/check_cubemx_survival.py`(§10.3)。
|
||||
> **2026-10-26(道G task-26)一句话登记,机制不在本文复述**:四个 Fault 与栈溢出 / 断言钩子的**崩溃上报发送口已归一**进控制台单写者(`dbg_console_write_raw` + 一次整行独占,不再自行调用 `HAL_UART_Transmit`),机制正文与残余见 `docs/子系统设计/控制台单写者日志设计说明.md` §4.4.1。本节只关心一件事:这些实现仍须整体待在 `Src/freertos.c` 的 `USER CODE BEGIN 4/5` 区与 `Src/stm32f4xx_it.c` 的 `USER CODE BEGIN 0` 区(§10.2 的 L2),改完跑 `scripts/check_cubemx_survival.py`(§10.3)。同族另一笔只登记指针:断言钩子与临界区在控制台路径上互达会构成**幻影环**,armlink 的 `In Cycle` 记账随环形状波动(同一 port.o 三棵树实测每链差 0~80 B,休眠≠解耦),三树取证、量化表与「configASSERT 裁空宏 ≠ 优化收益」的钉死句见 `docs/内存管理/RAM使用分析与优化方案.md` §4.0.7(道G task-38)。
|
||||
|
||||
### 10.1 覆盖史(三次重生成)
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
| `--head-char C` | 命中行(去残留后)必须以 C 开头,如 `[` | 崩溃日志的行首形态本身就是判据 |
|
||||
| `--pair FIRST THEN` | 恰好一条 FIRST,且其后**只有一条** THEN(跨复位配对) | 崩溃遗言必须与下一次启动的 `reset cause:` 成对;出现两条 = 有未记录/重复复位 |
|
||||
| `--include-comments` | 不跳过 `#` 行 | 默认**跳过**:归档证据文件带 ASCII 溯源头,头里提到 `[ERR]` 不该触发 Forbidden(工具级假红防线) |
|
||||
| `--selftest` | 跑内置 16 条判别用例后退出,无需日志与硬件 | "不可能失败的 PASS 就是空跑"——用例里含交错、缺行首、残留 1/2、配对错序,以及**"旧子串口径在同一条缺陷行上判 PASS"** 这一条盲区证明 |
|
||||
| `--selftest` | 跑内置 **22 条**判别用例后退出,无需日志与硬件(含下表三计数分类器) | "不可能失败的 PASS 就是空跑"——用例里含交错、缺行首、残留 1/2、配对错序,以及**"旧子串口径在同一条缺陷行上判 PASS"** 这一条盲区证明 |
|
||||
|
||||
用法示例(一次仅复位启动的原子性判定)。四个行级开关**也已接进 `run_boot_test.py`**,
|
||||
所以 @test 闭环里直接加 `--exact ... --pair ... --allow-residual 1 --head-char '['` 即可,无需单独跑本脚本:
|
||||
@@ -50,6 +50,22 @@
|
||||
--max-boot-count 1 --show-tail-lines 0
|
||||
```
|
||||
|
||||
**退出码与三计数(task-32 定稿)——判据必须自己说清「没启动」与「没采到」**:
|
||||
|
||||
| 码 | 含义 | 触发条件 | 聚合口径 |
|
||||
|---|---|---|---|
|
||||
| 0 | PASS | 标记齐、Forbidden 零、锚点合法 | 一次好启动 |
|
||||
| 1 | **设备侧真有坏消息** | 缺 Required / 命中 Forbidden / 顺序或耗时超限 / 整行锚定失配 | 记一次设备故障 |
|
||||
| 2 | **测量没成立** | 日志读不到;或开了 `--max-boot-count` 却**零启动锚点**(采集器没开、日志被清、采集没从复位开始) | **不得**计入设备故障 |
|
||||
|
||||
三计数读法:`hits=0 + loose_neighbours=0 + 锚点=0` = 采集断了,先修测量,此时任何通过都无意义;
|
||||
`hits=0 + neighbours≥1` = 行被啃(真缺陷,正是 `--exact` 要抓的);
|
||||
`锚点≥1 + hits=0 + neighbours=0` 才允许当"功能缺失"看。
|
||||
优先级铁律:**零锚点但同时有 Forbidden 命中或自检故障记录 → 仍判 1**,测量问题只在一边加 WARN
|
||||
——绝不能把一次真崩溃降级成「没数据」(实测已按此口径验过:造一份"零锚点但含 `[ERR]` MEMFAULT"的日志,
|
||||
工具仍判 FAIL 并另加一行 WARN,没有把崩溃改判成测量问题)。
|
||||
`--selftest` 里钉了 6 条分类器用例;把分类器改成常量返回"不坏",自检立刻红(真跑过)。
|
||||
|
||||
## 3. 固定动作(顺序即判据,别调)
|
||||
|
||||
1. **环境**:继承完整环境,只补 `DTU_KEIL_UV4` / `DTU_STLINK_CLI`,并把 `TEMP`/`TMP` 指到真实目录。
|
||||
@@ -70,10 +86,12 @@
|
||||
7. **非空跑对照**:每换一版校验器,至少跑一次 `--selftest`,并把**缺陷发生时的原始捕获**再判一遍——必须 FAIL。
|
||||
8. **收尾**:最后一次硬件动作是 `cmp`(排在全批日志之后);台账与证据按 `boot_log_data/README.md` 的口径归档。
|
||||
|
||||
## 4. 两条容易踩的纪律
|
||||
## 4. 三条容易踩的纪律
|
||||
|
||||
1. **故意复位的腿不得并入"连续仅复位不重烧"的计数**。崩溃注入腿(MemManage/栈溢出钩子)本身就要复位设备,
|
||||
并进常规序列会被下一个人数锚点数判成异常。它必须单独跑、单独进台账、标题写明"本腿含故意复位"。
|
||||
**同一条口径覆盖"前面带烧录的圈"**:烧录启动之后紧跟的那一次启动不属于"连续仅复位不重烧",
|
||||
报数写成两句并列——「烧录启动 ×1(不进序列)+ 仅复位 ×N」;若那一圈连字节都没采到,则**两条独立理由**都排除它(没数据谈不上计数)。
|
||||
2. **崩溃腿的判据是原子性,不是 `[ERR]` 为零**。内核故障钩子的输出**本身就以 `[ERR]` 开头**
|
||||
(`[ERR] MEMFAULT …` / `[ERR] STACK OVERFLOW task=…`),在同一条路径上要求 `[ERR]=0` 等于要求它别工作;
|
||||
而**产品构建的正常启动根本不走 IPSR 路径**(多段裸发只在 `Src/freertos.c` 的两个内核钩子里),
|
||||
@@ -97,7 +115,17 @@
|
||||
- ⚠ **脚手架 `*.json` 一律不得放 `_wt/.state/`**:那是 task-worktree 的**台账目录**,脚本把每个 `*.json`
|
||||
当台账读,实测 `task_worktree.py list` 直接崩在 `KeyError: 'id'`。放 `_wt/scratch/`。
|
||||
|
||||
## 7. 关联
|
||||
## 7. 台架卫生清单(每一条都是实测踩过的,不是设想)
|
||||
|
||||
1. **每个台架窗口开头、以及每一圈复位之前**:`serial_tool.py open` + `status` 断言 `port.ready=true`,并**观察 `rx_lines` 在几秒内增长**。
|
||||
实测:常驻 daemon 会死(今晚 pid 18020 跑了 156589 s 后没了),而 `status` 会**自动拉起一个新 daemon**,于是它一直"正常"地报 `ready=false / rx_lines=0` —— 端口状态**不会**跨小时存活,`@test` 开过也不算数。
|
||||
2. **`serial_tool.py log / status / clear` 返回 JSON,不是纯文本**。按 `[RX]` 行首过滤纯文本会得到**恒 0 行**且看起来"跑成功了"。
|
||||
3. **Forbidden 令牌不得落在英文单词内部**:裸 `Fault` 命中 `de`+`fault` → 打到 `[FTP] using factory default password` 与 `[STACK] default peak_used`(实测假红两次)。四个 Fault 的崩溃日志都以 `[ERR]` 开头,列 `[ERR]` 就够,要加就加 `MEMFAULT/BUSFAULT/USAGEFAULT/HardFault`。
|
||||
4. **工具输出别混流**:非零退出时 stdout(结果)与 stderr(诊断)拼接后再 `JSON.parse` 必崩;解析只吃 stdout。
|
||||
5. **给同事/lead 发消息时,正文里的引号用全角「」**:在 JS/TS 双引号字符串里放 ASCII `"` 会直接语法错,消息发不出去(本人一晚三次)。
|
||||
6. **判据落点顺序**:先 grep 谁在消费这个码/字段(今晚的唯一消费者 `run_boot_test.py` 只对 `rc==0` 分支),再决定动不动语义。
|
||||
|
||||
## 8. 关联
|
||||
|
||||
- 修法与设计口径:`../子系统设计/控制台单写者日志设计说明.md`(O1 两实例与字节账、§4.4.1 崩溃路径验证设计)
|
||||
- 闭环与标记口径:`./自动化测试流程.md`
|
||||
|
||||
@@ -437,6 +437,21 @@ CH395F / STORAGE / GD5F,未含 RTC;怀疑 RTC 构建下 `testTask` 不被创
|
||||
(t≈0.6s / t≈2.8s,snap 仅缺 UI 位),NET_READY 解除后立即恢复——**未达 800ms 停喂
|
||||
阈值、不触发复位**,属分片心跳与 100ms 抽样边界的固有交错,非回归;
|
||||
处置:见到该行不必恐慌,**连续**出现 `task stall` 行/复位才是真停摆。
|
||||
9. **导航类自动化脚本默认零 ENTER**(2026-10-26 台架实证,task-41 收口;口径也见
|
||||
`启动日志原子性复验流程.md`):装置维护的 **强制复归 / 清除记录 是两段式确认**——
|
||||
第一次 ENTER 只把该行标成「确认?」,**第二次 ENTER 才真执行**(复归保护位并写 SOE /
|
||||
清 SOE·运行·操作记录)。因此任何"认不出屏就按 ENTER 试一下"的回收逻辑,落到这两页上
|
||||
等于**替操作员按下执行键**;更隐蔽的是它只在画面异常时才发作(新字库缺字、白框、状态行
|
||||
变化——恰恰是回归最想抓的那几帧),平时零流量、改动时无声。 `scripts/board_ui_nav.py`
|
||||
的两条硬闸与 `--selftest-quick` 里的 **7 条常驻用例**(假设备 + 内存空白帧 + 按键只记账,
|
||||
**不连 7003、不开串口、不碰台架**,提交钩子 `uinav-selftest` 就跑这档)把它钉成回归:
|
||||
① UNKNOWN 退避的 ENTER 上限 `BACKOFF_MAX_E`(政策钉死 ≤1,需要第二次就当场停机并 dump 帧,
|
||||
报错文案点名执行类风险);② `EXEC_PAGE_IDS`(clear / reset)**默认拒绝下钻**且拒绝发生在
|
||||
**任何键投出去之前**(真机实测:连轨迹文件都不生成),显式 `--allow-exec-pages` 才放行并打警告。
|
||||
用例还钉住**两个常量本身**:上限被调到 2 以上、清单被清空、或默认值被翻成"允许"——
|
||||
都必须在 `--selftest-quick` 里报红(这三条都用"故意改坏再跑"逐条证过能红,不是空跑)。
|
||||
通用要求:**导航 = 移光标 + 取帧;ENTER 只能出现在明确、非执行类的用例步骤里**;
|
||||
要看执行类页面的显示正确性,去模拟器黄金参考(那里没有真机后果)。
|
||||
|
||||
---
|
||||
|
||||
@@ -481,6 +496,7 @@ CH395F / STORAGE / GD5F,未含 RTC;怀疑 RTC 构建下 `testTask` 不被创
|
||||
|
||||
| 日期 | 变更 |
|
||||
|---|---|
|
||||
| 2026-10-27 | **§9 新增第 9 条「导航类自动化脚本默认零 ENTER」**(task-41):装置维护两页是两段式确认、第二次 ENTER 即真执行,故导航器禁止用 ENTER 从未知状态脱身;`scripts/board_ui_nav.py` 两条硬闸(`BACKOFF_MAX_E` ≤1 停机、`EXEC_PAGE_IDS` 默认拒绝且投键前即拒)做成 `--selftest-quick` 7 条常驻用例(假设备零注键,提交钩子跑这档),并钉住常量本身(上限调高 / 清单清空 / 默认翻成允许 → 报红,三条均实测能红) |
|
||||
| 2026-10-03 | **步骤 5/6/7 闭环**:§6 GD5F 错误通道回归 PASS(`=== GD5F2GQ5UE Tests: 16/16 PASSED (failed=0) ===`;E-10/42/53/54/56/57 全部销项;顺带发现并修复编排器判据缺口——GD5F 的 TC-GD5F-704 故意触发 `[ERR]` 回落轮询,`run_all_suites.py` 改为**按套件**覆盖 `--forbidden`,与 `verify_log.py` 说明一致)→ §1/§6 改「✅ 已闭环」。§7 网络稳定性:**7.1 正常路径 ✅**(V5 120/120 FTP、零复位;负向路径转 `N-16`,台架拓扑受限);**7.2 N-2 ✅判据②/未复现判据①**(丢弃可观测性代码已有:`console_log.c` `s_con_drop`+`[ERR] CONSOLE dropped`、`sys_log_mcu.c` 队列满 `DBG_ERROR`、TX 僵尸通道自愈;V5 30 min 日志间隔分析最大静默 41s 且在开机边界、运行期 24 处 >10s 静默**全为 ~28s 且均在 `e02_probe` 窗口内**=N-17 同源)→ N-2 降级低风险观察项;**7.3 N-3 ⬜ 待 PC peer** → §1/§7 改「◐ 部分闭环」。§8 工程卫生:S5 `test-cppcheck` 钩子 PASS(仅 warning,排除模拟器桩);S4 `scripts/check_test_traceability.py` 落地并跑通(**30 处差异**,默认 warn-only);**待核实项 `TEST_SUITE_SELECTED` 实机核实 = 死代码**(全仓无使用点;`start_test_task()` 直接按宏分支,RTC 套件实际正常执行;真缺口 = 缺 `=== RTC Tests: N/M PASSED ===` 汇总行,已补 `test/board/sd2506_test_task.c` 并修正 Phase 级汇总累计问题;`test/board/test_config.h` 补齐 RTC 并注释"当前无引用")→ §1/§8 改「✅ 已闭环」 |
|
||||
| 2026-10-03 | **步骤 3/4 闭环**:V2/V4/V5 台架验收通过(V2 任务饿死声明 stall 后 ≤2.5s `reset cause: IWDG`;V4 关中断死循环每 ~4.9s 稳定 IWDG 复位;V5 30 min / **120×FTP 全 PASS、零 IWDG 复位**)→ §1 总览与 §4 状态改「✅ 已闭环」,实施细节与证据移入 `子系统设计/看门狗设计说明.md` 变更记录。S11 堆告警代码落地、V5 全程未触发(`min_ever=4728 B` > 3072 B)→ §5 改「✅ 已闭环」,基线入 `内存管理/RAM使用分析与优化方案.md`。新登记 `待处理内容.md` **N-17**(V5 期间 44 次 stall 与 `e02_probe` 的 time_sync 30s 掉线+监听重建重合;隔离实验「仅 FTP 120s = 0 stall」证实探针场景专属) |
|
||||
| 2026-10-03 | **重构为纯执行流程**:确立单一组织逻辑——本文 = **按顺序执行**的上板测试流程(步骤 1~7 + 状态标记),参考内容(§9 执行注意 / §10 已完成事项去向 / §11 引用闭环)后置。已完成事项(S7、A1~A4)的实施细节与真机证据**移出本文**,§10 只留「事项 → 去向」指针;§3 只留复跑命令与判据;§1「事项总览」改「执行流程总览」(步骤/状态列) |
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
| `.dsh/skills/serial-boot-test/scripts/run_boot_test.py` | `.dsh/skills/serial-boot-test/scripts/run_boot_test.py` | **一键闭环**:open + clear → 烧录 → 固件身份戳(从 hex 抠 `fw build:`)→ wait-idle(静默 8s/超时 30s/首字节 12s)→ 提取 `[RX]`(每行加 `[+Nms]` 时基)→ 回读校验(`stlink_flash.py cmp`)→ verify;`--keep-out` 清旧产物;退出码 0=PASS/1=FAIL/2=采集·烧录·回读错误/3=用法错误 |
|
||||
| `.dsh/skills/stlink-flash/scripts/stlink_flash.py` | `stlink-flash` skill | ST-LINK CLI 封装:`flash [hex]`(烧 + `-Rst -Run`)/ **`cmp [hex]`(回读比对,2026-10-10 加)**/ `reset`(只复位)/ `hard`(nRST 脉冲,CLI 拒 `RM=` 自动降级)/ `halt`/`run`/`list`;原 `MDK-ARM/reset.bat` 已于 2026-10-10 删除并全并入本 skill |
|
||||
| `.dsh/skills/serial-assistant/scripts/serial_tool.py` | `.dsh/skills/serial-assistant/` | Python 串口助手(常驻 daemon + 薄 CLI):ports/open/send/status/log/clear/wait-idle/selftest;全量日志 `state/serial.log`(20MB 自动截头) |
|
||||
| `.dsh/skills/serial-boot-test/scripts/verify_log.py` | `.dsh/skills/serial-boot-test/scripts/verify_log.py` | 对日志做标记校验:Required/Forbidden(**命中逐行打印行号+内容**)、启动锚点计数 `--max-boot-count`、原因白名单 `--expect-reset-cause`、标记顺序 `--require-order`、开机耗时 `--done-marker`/`--max-boot-ms`、`--json` 结构化输出、**整行锚定 `--exact` / 跨复位配对 `--pair` / 崩溃腿 `--allow-residual`+`--head-char` / 判别自检 `--selftest`**;退出码 0/1/2。子串 Required 照不出"行首被吃掉"那一类行级损坏 → 行级完整性一律用 `--exact`,口径与固定动作见 `启动日志原子性复验流程.md` |
|
||||
| `.dsh/skills/serial-boot-test/scripts/verify_log.py` | `.dsh/skills/serial-boot-test/scripts/verify_log.py` | 对日志做标记校验:Required/Forbidden(**命中逐行打印行号+内容**)、启动锚点计数 `--max-boot-count`、原因白名单 `--expect-reset-cause`、标记顺序 `--require-order`、开机耗时 `--done-marker`/`--max-boot-ms`、`--json` 结构化输出、**整行锚定 `--exact` / 跨复位配对 `--pair` / 崩溃腿 `--allow-residual`+`--head-char` / 判别自检 `--selftest`(22 条,含三计数分类器)**;退出码 **0=PASS / 1=设备侧真有坏消息 / 2=测量没成立**(读不到日志,或开了锚点计数却零锚点——采集断了不算设备故障)。子串 Required 照不出"行首被吃掉"那一类行级损坏 → 行级完整性一律用 `--exact`,口径与固定动作见 `启动日志原子性复验流程.md` |
|
||||
|
||||
## 判定标准
|
||||
|
||||
|
||||
@@ -85,10 +85,73 @@ $env:DTU_STLINK_CLI = 'D:\Tools\ST-LINK Utility\ST-LINK_CLI.exe'
|
||||
|
||||
---
|
||||
|
||||
## 5. 三个解释器各能跑什么(2026-10-27 实测,道G task-33)
|
||||
|
||||
> 起因:有队友在 `.venv` 里 `import identify.tags` 失败,又用 `pip` 查不到 `pre-commit`,据此判定
|
||||
> 「`.venv` 里的 pre-commit 安装是残缺的」。**复测结论:环境不残缺,两条观测都成立但推错了因**——
|
||||
> 前者是版本本身没有那个模块(下表第三行),后者是查错了对象(下表第二行)。
|
||||
> 取证方法固定:同一个探针脚本(`sys.executable` + 逐个 `import` + `importlib.metadata.version`)
|
||||
> 分别喂给三个解释器;脚本放仓外临时目录,不入库、不改任何环境。
|
||||
|
||||
| 解释器 | 身份 | 门禁相关模块 | 第三方包 | 结论 |
|
||||
|---|---|---|---|---|
|
||||
| `.venv\Scripts\python.exe` | 3.12.4(**基底是 Anaconda**,非干净 CPython) | `identify` 可导入、`pre_commit` 可导入;`identify.tags` **无此模块** | fontTools 4.51.0 / pillow 10.3.0 / pyserial 3.5 / pyyaml 6.0.3 / cfgv 3.5.0 全在,`pre-commit` 与 `identify` 的 dist-info 齐全(`pip show` 可查) | **就是钩子的执行环境**:`.git/hooks/pre-commit` 里 `INSTALL_PYTHON` 写死指向它 |
|
||||
| `C:\Python314\python.exe` | 3.14.7,PATH 首位 | 三个全缺 | **一个都没装**(`pip` 元数据全 `PackageNotFoundError`) | 「查不到 pre-commit」的现场就是它;`language: system` 的 42 条 entry 的裸 `python` 落在这里 |
|
||||
| `C:\Users\...\Python311\python.exe` | 3.11.9,PATH 次位 | 同上全缺 | 同上全缺 | 与门禁无关,列出来只为排除「装了但找不到」的猜测 |
|
||||
|
||||
**为什么"缺 `identify.tags`"不是故障**:`identify-2.6.19.dist-info/RECORD` 一共 24 行,列出的模块只有
|
||||
`__init__.py` / `cli.py` / `extensions.py` / `identify.py` / `interpreters.py` / `vendor/licenses.py`——
|
||||
**根本没有 `identify/tags.py` 这一条**,而磁盘上的文件与 RECORD 逐项一致。即"元数据完好、文件齐全、
|
||||
只是这个版本没有那个模块"。标签常量在这个版本里位于 `identify/identify.py`(`TEXT` / `BINARY` /
|
||||
`EXECUTABLE` / `ALL_TAGS`),pre-commit 4.6.2 自己也是从那里取的(`clientlib.py` 里
|
||||
`from identify.identify import ALL_TAGS`),**全仓没有任何脚本 import `identify.tags`**(实测 grep 零命中)。
|
||||
⇒ 需要复算文件类型标签时,正确入口是 `identify.identify`;旧写法 `identify.tags` 在这个版本对上必然失败。
|
||||
|
||||
### 5.1 新增钩子 entry 的硬规则(今天没炸的真正原因)
|
||||
|
||||
`language: system` 的 entry 不继承 `.venv`:pre-commit 不改 `PATH`,所以裸 `python` 命中 PATH 首位
|
||||
(本机 = 3.14.7,**零第三方包**)。实测本仓 44 条 system 钩子里 **42 条 entry 以裸 `python` 开头**,
|
||||
今天能全绿只因为两件事同时成立:
|
||||
|
||||
1. 那 42 条脚本**只用标准库**(抽测 bom-drift / layer-deps / doc-links 与一套带 harness 的主机单测,
|
||||
在 3.14.7 下 exit 0);
|
||||
2. 唯一需要第三方包的那条(字体幂等性钩子 `scripts/check_generated_fresh.py`)**自带解释器解析**,
|
||||
不依赖 entry 用的是谁。
|
||||
|
||||
⇒ 写新钩子时二选一,别开第三条路:要么脚本只用标准库,要么在脚本内部按
|
||||
「显式变量 > 当前解释器 > 仓库 `.venv` > PATH」自行解析并**探针校验**(先例与理由见
|
||||
`docs/门禁说明/generated-fresh.md` §4)。**不要**把 entry 写死成某台机器的绝对路径。
|
||||
|
||||
### 5.2 什么情况下才需要动环境,以及动之前要走死顺序
|
||||
|
||||
默认动作是**不动**(本节存在即结论:没有需要重装的东西)。只有出现下列**具体现象**才执行修复,
|
||||
且必须先要到静默窗口——`pre-commit` 与 `.venv` 是全队共享运行面,中途换包会把别人的一次钩子跑打断:
|
||||
|
||||
1. 停表:确认没有人在跑提交/门禁/台架(build 与 bench 两类锁都空闲,见 `task-worktree`);
|
||||
2. 备份指针:记下 `& '.venv\Scripts\python.exe' -m pip freeze > 仓外文件`(不入库);
|
||||
3. 只补不重建:优先 `pip install --force-reinstall --no-deps <包>==<requirements.txt 里的版本>`,
|
||||
**不要**顺手 `-U`(那会连带动别人的依赖);确实要整体重建才 `pip install -r requirements.txt`,
|
||||
且该文件必须保持纯 ASCII(Windows 下 pip 按代码页解码);
|
||||
4. 自证(缺一不算修完):`& '.venv\Scripts\python.exe' -m pip show pre-commit` 有版本 →
|
||||
`-m pre_commit --version` 通 → `scripts/check_generated_fresh.py` 与字体字符集钩子在 `.venv`
|
||||
与 PATH 两种解释器下都 exit 0 → 最后跑一次 `scripts/ci_local.py`;
|
||||
5. 回滚:新装/升级出问题时按第 2 步的 freeze 反向 `pip install <包>==<原版本>`;**别删 `.venv` 重来**,
|
||||
删了就把「基底是 Anaconda」这条不可复制性也一起删掉(换机复现不了,见本文 §3 与 `docs/环境与部署/项目部署环境要求.md`)。
|
||||
|
||||
### 5.3 仍开着的账(不在本次范围,登记给对应属主)
|
||||
|
||||
- `DTU_PRECOMMIT_PYTHON` 本机当前**未设**,靠 §1.1 的优先级自动落到 `.venv`;如果哪天 PATH 首位换了
|
||||
解释器,静态门禁档就会跟着换——把它显式设成 `.venv` 是一条零成本的加固(改的是本机环境,不动仓库)。
|
||||
- 42 条裸 `python` entry 与两个解释器的版本差(3.14 vs 3.12):今天没有可观测的行为差,但这是
|
||||
「靠巧合不炸」。要不要把 entry 统一改成显式解释器解析,属门禁属主的决定(同一族的先例见 §5.1)。
|
||||
- 本机的 `.venv` 基底是 Anaconda 发行版,`python -m venv` 在别的机器上给出的不是同一个东西;
|
||||
这条差异目前无人记录在 `docs/环境与部署/项目部署环境要求.md` 里。
|
||||
|
||||
|
||||
## 变更记录
|
||||
|
||||
| 日期 | 变更 |
|
||||
|---|---|
|
||||
| 2026-10-02 | **文档改造**:删除规划阶段内容(设计结论、路径耦合盘点、代码改造映射、分阶段推广与验收、风险与边界、后续任务拆分),保留并重组为现行机制说明——§1 变量清单(含 Python 优先级特别说明)、§2 解析与失败语义、§3 本机配置方法(会话/用户/runner 三级)、§4 验收判据、变更记录;章节重编号,外部引用同步(`gen_refgraph.py` §3→§1、`ci_local.py` §3.1→§1.1、`Gitea_Runner部署指南.md` §5.3→§3.3) |
|
||||
| 2026-10-27 | **新增 §5 解释器实测矩阵(道G task-33,只读诊断,未动任何环境)**:三个解释器(仓库 `.venv` / PATH 首位 / PATH 次位)逐个探针实测,钉住三条事实——① `.venv` **不是残缺**:`pre-commit` 与 `identify` 的 dist-info 齐全(`pip show` 可查),`identify-2.6.19` 的 RECORD 里本就没有 `tags.py`,标签常量在该版本的 `identify.identify` 里,pre-commit 自己也是从那里取(旧写法 `identify.tags` 在这个版本对上必失败,全仓零引用);② 钩子执行环境被 `.git/hooks/pre-commit` 的 `INSTALL_PYTHON` 钉死为 `.venv`,而 `language: system` 的 42 条 entry 用裸 `python` → 落到 PATH 首位那个**零第三方包**的解释器,今天没炸是「42 条全只用标准库 + 唯一需要第三方包的那条自带解释器解析」两件事同时成立,据此写下新增 entry 的硬规则(§5.1);③ 「查不到 pre-commit」的现场是查了 PATH 上的解释器。另附 §5.2「默认不动环境;确需修复时的死顺序与回滚(只补不重建、freeze 备份放仓外、按 freeze 反向回滚、禁止删 `.venv` 重来)」与 §5.3 三条待属主的账。| 2026-10-02 | **文档改造**:删除规划阶段内容(设计结论、路径耦合盘点、代码改造映射、分阶段推广与验收、风险与边界、后续任务拆分),保留并重组为现行机制说明——§1 变量清单(含 Python 优先级特别说明)、§2 解析与失败语义、§3 本机配置方法(会话/用户/runner 三级)、§4 验收判据、变更记录;章节重编号,外部引用同步(`gen_refgraph.py` §3→§1、`ci_local.py` §3.1→§1.1、`Gitea_Runner部署指南.md` §5.3→§3.3) |
|
||||
| 2026-10-02 | **实施完成**:阶段 A~D 落地。① 10 处调用点全部改为"`DTU_*` 变量优先 + 自动发现回退"(显式值无效 = 硬报错、不静默回退):`build/download/reset.bat`(goto 标签式解析,纯 ASCII)、`ci_local.py`(`DTU_PRECOMMIT_PYTHON` 严格校验)、`ci.yml`(`DTU_PRECOMMIT_PYTHON`/`DTU_CPPCHECK`/`DTU_MSVC_CL`/`DTU_WINDOWS_KITS_ROOT`,run 块纯 ASCII)、`gen_refgraph.py`(`DTU_GRAPHVIZ_DOT`→PATH,保留无渲染降级)、`unit_test_harness.py`(`DTU_MSVC_CL`/`DTU_WINDOWS_KITS_ROOT` + `%ProgramFiles*%` 推导取代静态盘符列表)、`build_sim.ps1`(`DTU_VSWHERE`→`%ProgramFiles(x86)%` 推导,去字面 C 盘回退)、`make_font.py`(`%WINDIR%` 推导);`_build.cmd` 确认零调用后删除。② 本机按 §3.2 配置用户级 `DTU_KEIL_UV4`/`DTU_STLINK_CLI`(其余工具本机均可自动发现,未设变量),验证:`@build` BUILD_OK 0/0、`ci_local.py` L0 15 钩子 + L0.5 278/278 断言全绿、refgraph 重生成(含修复 `引用关系图.json` 末尾换行缺失导致的 end-of-file-fixer 抖动,生成器补 `f.write("\n")`)、`build_sim.ps1` vswhere 推导命中 VS2026(当时模拟器 CMake 配置期暴露**既有**问题:`App/cfg_file.c` 已移入 `App/cfg/` 但 `simulator/CMakeLists.txt` 未同步——与环境变量改造无关;2026-10-02 已修复:CMakeLists 同步 `App/cfg/` + `test/board/` 引用,模拟器全量构建通过)、`reset.bat list` 识别探针。③ 文档收口:`AGENTS.md`/`MDK-ARM/README.md`/`项目部署环境要求.md`(新增 §2.1.1)/`Gitea_Runner部署指南.md`(新增 §5.1.3 机器级变量)/`CI配置指南.md`/`pre-commit使用说明.md`/字体/测试流程/引用图说明/ST-LINK 说明/实机规划/`scripts/README.md` 共 12 篇同步为变量口径。④ `requirements.txt` 增 `pre-commit==4.6.2`(重建 `.venv` 即可跑 L0)。⑤ 全量 `pre-commit --all-files` + `ci_local.py` 通过。待办:新 workflow 推送远程后跑一次 CI 两 job 闭环验证;第二台不同安装路径机器的交叉验证(操作清单已固化入 `项目部署环境要求.md` §2.12) |
|
||||
| 2026-10-02 | 初版规划:盘点 BAT/Python/PowerShell/Gitea workflow 中机器相关工具路径;定义 `DTU_*` 变量清单、优先级、用户级与 SYSTEM runner 配置边界;规划 4 阶段迁移及离线/实机验收范围。未改工具调用代码 |
|
||||
|
||||
Reference in New Issue
Block a user