task-33 诊断结论的落地(道G 执行,环境**一个字节没动**:不装包、不重建、不改 requirements 约束)。
**要防的是什么**:`.git/hooks/pre-commit` 把 `INSTALL_PYTHON` 钉在 `.venv`,而 43 条
`language: system` 用
裸 `python` ⇒ pre-commit 本体走 `.venv`(3.12.4)、钩子 entry 走 PATH 首位(3.14.7,零第
三方包)。今天
不炸是**巧合**(那 43 条模块级 import 全是标准库,唯一要 fontTools 的那条自己解析解释器)。真正的危
险形态是:某条钩子在**跑到它那一刻**才报 `No module named ...`——一次提交里前 14 条已经改写完工作区、
第 15 条才崩,现场最难看,也最容易被误当成代码问题。L0a 把这件事提前到门禁第一段。
**FAIL 判据刻意收窄成三事齐备**:模块级第三方 import + 该脚本不自解析解释器 + PATH 首位那个解释器
真缺包。惰性/条件导入(`make_font` 的 fontTools 在函数体内)只列为**候选**、不判决——否则就是假阳性
(见下)。退出码 2 = 环境错误,已写进文件头退出码段与用法。
**道G 自己推翻过自己两处,都留在文档里没删**:
① 上一版诊断说「42 条里唯一需要第三方包的那条自带解析」是**宽松正则**审出来的(把注释与字符串里的包
名也算命中),机械口径下按模块级 import 判是 **0 条**,条目数也因 nested-comment 落地从 42 变 43;
② 第一版 L0a 把 font-charset 判成 FAIL,他拿真解释器实跑反证(`C:\Python314\python.exe
scripts/check_font_charset.py` = exit 0,因为 fontTools 的导入在函数体内、加载期不执行)
⇒ 判据改窄。
**红-绿自证**:临时假仓库三条钩子(要 fontTools 且不自解析 / 同样要但自解析 / 纯标准库)→ 坏的那条判
FAIL、好的判 OK、整体 rc=2;真实仓 → 43 条、forced 缺包 0、候选 fontTools、exit 0;同一预检在 .venv 与
PATH 首位两个解释器下都 exit 0。
文档 §5 重排成**依赖表(§5.2) → 动之前死顺序(§5.3) → 遗留账(§5.4)**(原文按追加顺序把失效顺序插成了
§5.4,读起来先结论后依据;重排时同步修掉块内那条会指错的「见下面 §5.3 第三条」,以及 ci_local 里指向
死顺序却写着 §5.2 的指针——这两处 doc-links 都**静默放过**,因为本文恰好有同号小节,见 §3.5 盲区)。
四种现场(删/换 `.venv`、只换包、换 PATH 首位、改 requirements 约束)逐行钉死「第一现场 / 症状 / 还能
动什么」,回滚按现场分条不混用:freeze 备份放仓外、只 `--force-reinstall --no-deps` 单包不 `-U`、
`pre-commit clean` 会重建别人的钩子环境所以必须先要窗口、最后手段才删 `.venv`(删前记住基底是 Anaconda,
换机复制不出来)。
⚠ 一条与本笔无关的既有红灯(道G 报出,我核过成因):`check_size_gate.py --local` 现在 exit 1 —— 它按
**git 索引 blob** 生成代码指纹,而工作区正有 comm 道在飞的 `App/protocol/iec104/iec104_comm
.{c,h}` 未入
索引,「同码」必然不成立;**该脚本不是 pre-commit 钩子**(只挂在 ci_local),所以不拦提交,但别人跑
ci_local 会看到它,别误判成 L0a 新加的东西。台账回填等这批在飞代码提交后统一做一次。
自查:py_compile 0、`--preflight-only` exit 0(两个解释器下都是)、`check_doc_links --a
ll` exit 0(198 篇)、
本篇 `--sections-strict` 通过、`id-check --strict` exit 0 且不可判定绝对数 **978 未变**
(写文档没吃余量)、
mojibake / bom(81) / nested-comment(409) 全 0、尾随空白 0。
22 KiB
本地工具路径环境变量方案
本文档描述项目的本机工具路径环境变量机制:变量清单、解析与失败语义、各级配置方法、验收判据 (2026-10-02 实施并生效;实施过程与后续变更见文末变更记录)。 约束(现行):只用进程/用户/CI 服务环境变量配置本机差异;不提交本机路径,不引入
.env/python-dotenv, 不把个人工具路径写入共享 workflow;仓库内部路径继续相对脚本/仓库根解析。
1. 变量清单
| 变量 | 必需性 | 值的形态 | 使用者 | 默认/回退 |
|---|---|---|---|---|
DTU_KEIL_UV4 |
MCU 构建机必需 | UV4.exe 完整路径,例如 D:\Tools\Keil\UV4\UV4.exe |
keil_build.py(@build) |
不设即报错(exit 2);不从 C 盘旧路径猜测 |
DTU_STLINK_CLI |
复位/烧录/回读机必需 | ST-LINK_CLI.exe 完整路径,例如 C:\Program Files (x86)\STMicroelectronics\STM32 ST-LINK Utility\ST-LINK Utility\ST-LINK_CLI.exe |
stlink_flash.py |
未设时回退 PATH(where ST-LINK_CLI.exe 首个命中);已设但无效 = 硬报错 |
DTU_PRECOMMIT_PYTHON |
可选(覆盖用) | 含 pre_commit 模块的 python.exe 完整路径 |
ci_local.py L0、CI l0-gates |
未设时按 §1.1 探测(当前解释器 → .venv → PATH);已设但无效 = 硬报错 |
DTU_GRAPHVIZ_DOT |
可选(覆盖用) | dot.exe 完整路径 |
gen_refgraph.py |
未设时只查 PATH;无 Graphviz 时降级为"生成 .dot/.json、跳过渲染" |
DTU_MSVC_CL |
可选(覆盖用) | x64 cl.exe 完整路径(bin\Hostx64\x64 下) |
unit_test_harness.py L0.5、CI l05 |
未设时按 VS 安装根自动探测(2019/2022/2026 各 edition);已设但不符 bin\Hostx64\x64\cl.exe = 硬报错 |
DTU_WINDOWS_KITS_ROOT |
可选(覆盖用) | Windows Kits\10 根目录(须含 Include/Lib) |
unit_test_harness.py L0.5、CI l05 |
未设时从 %ProgramFiles*% 推导根自动探测;已设但无 Include/Lib = 硬报错 |
DTU_VSWHERE |
可选(覆盖用) | vswhere.exe 完整路径 |
build_sim.ps1 |
未设时用 %ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe 推导;已设但无效 = 硬报错 |
DTU_CPPCHECK |
可选(覆盖用) | cppcheck.exe 完整路径 |
CI l0-gates cppcheck 步骤 |
未设时用 PATH(Get-Command);已设但无效 = 硬报错。本地 ci_local.py L0 不读该变量:cppcheck 钩子按 pre-commit entry: cppcheck 从 PATH 解析 |
变量值只存路径本身(不加引号);可执行文件变量存
.exe完整路径,目录变量(Kits 根)存目录。 不设任何变量时全部走自动发现(PATH / 系统目录推导 /.venv/vswhere);变量只用于覆盖非标准安装位置。 字体系统目录(simsun.ttc)不设 DTU 变量:由%WINDIR%\Fonts\推导,make_font.py的--font参数仍最高优先。
1.1 Python 优先级特别说明(ci_local.py / CI L0)
pre-commit 是门禁的执行环境而非"工具地址",优先级与其余工具不同:
- 显式
DTU_PRECOMMIT_PYTHON(若设置,必须可执行且能import pre_commit,否则报错); - 当前解释器(
sys.executable,即实际运行ci_local.py的 Python); - 仓库
.venv\Scripts\python.exe(若存在且装了 pre-commit); - PATH 中的
python。
CI 侧同理:DTU_PRECOMMIT_PYTHON → runner PATH → workspace .venv。
2. 解析与失败语义(所有调用端统一)
- 显式值校验:变量已设置时,先校验其指向的文件/目录真实存在且符合形态(如
cl.exe必须位于bin\Hostx64\x64\下);无效则立即报错,错误信息必须包含变量名与所给值,不得静默回退到自动发现。 - 未设置才自动发现:PATH / 系统目录推导 /
.venv/vswhere等;自动发现失败按各工具既有降级或报错语义处理。 - BAT 实现约束:
.bat中读取环境变量用%DTU_...%;判空用if defined;错误分支exit /b非零。BAT 内解析一律平铺goto标签风格,不用括号块(echo )会提前闭合块)。 - PowerShell 实现约束:
$env:DTU_...;build_sim.ps1的 CMake 配置不直接读环境变量(保持生成器无关)。 - workflow run 块:
ci.yml的 run 块内用${env:DTU_...}(未设置时为空串)与 PowerShell$env:DTU_...判空,不得在 workflow 文件里写死任何本机绝对路径。 - Windows 命令行引号规则:值含空格(如
C:\Program Files (x86)\...)时,调用端必须整体加引号("%DTU_KEIL_UV4%"/"& $env:DTU_...");变量值本身不要带引号。 - 系统变量推导:
%WINDIR%、%ProgramFiles%、%ProgramFiles(x86)%仅用于推导(如make_font.py的simsun.ttc、build_sim.ps1的 vswhere 路径),不得作为"工具安装目录"的硬编码兜底。
3. 本机配置方法
3.1 当前 PowerShell 会话(立即生效,适合试配置)
# 仅当前会话有效;换终端/重启即失效
$env:DTU_KEIL_UV4 = 'D:\Tools\Keil\UV4\UV4.exe'
$env:DTU_STLINK_CLI = 'D:\Tools\ST-LINK Utility\ST-LINK_CLI.exe'
# 验证:Test-Path $env:DTU_KEIL_UV4 → True
3.2 用户级持久配置(日常开发机,推荐)
# 当前用户、全部新进程可见;无需管理员
[Environment]::SetEnvironmentVariable('DTU_KEIL_UV4', 'D:\Tools\Keil\UV4\UV4.exe', 'User')
[Environment]::SetEnvironmentVariable('DTU_STLINK_CLI', 'D:\Tools\ST-LINK Utility\ST-LINK_CLI.exe', 'User')
# 已存在同名值时直接覆盖;删除用 [Environment]::SetEnvironmentVariable('DTU_KEIL_UV4', $null, 'User')
# 设置后需重开终端/IDE 才对新进程生效
3.3 Gitea Windows runner(SYSTEM 服务)
# 必须管理员;写入机器级(Machine)环境,供 Gitea runner 服务(SYSTEM 账户)读取
[Environment]::SetEnvironmentVariable('DTU_PRECOMMIT_PYTHON', 'C:\ProgramData\anaconda3\python.exe', 'Machine')
[Environment]::SetEnvironmentVariable('DTU_CPPCHECK', 'C:\Program Files\Cppcheck\cppcheck.exe', 'Machine')
# 写入后需重启 Gitea runner 服务/任务才能带上新变量(服务启动时读取环境变量快照)
注意:runner 以 SYSTEM 运行,用户级变量对 runner 不可见;反之机器级变量对交互用户可见但不应依赖。开发机日常配置用 User 级,runner 专用覆盖用 Machine 级。 值示例中的
C:\ProgramData\anaconda3\...仅演示"显式覆盖"形态;若 runner 的 PATH/.venv可自动发现,则不必设置该变量。
4. 验收判据
- 新电脑只配置本机
DTU_*环境变量(§3),clone 仓库后可直接运行各开发命令,不修改任何脚本/工作流中的本机路径。 - 显式变量指向无效路径时,命令以非零退出码失败且错误信息指明变量名,不静默使用其他工具。
- 仓库内不出现提交的本机绝对工具路径(文档中的示例路径除外)。
- 跨机验证操作清单见
项目部署环境要求.md§2.12;runner 侧机器级变量设置见docs/CI/远程CI完整指南.md§4.1.3。
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,零第三方包)。实测本仓 45 条 system 钩子里 43 条 entry 以裸 python 开头,
今天能全绿的原因是(2026-10-27 二次修正,改以机械口径为准):
- 这些条目没有任何一条在模块级 import 第三方包(抽测 bom-drift / layer-deps / doc-links 与一套 带 harness 的主机单测,在 3.14.7 下 exit 0);
- 真正会碰第三方的是下游脚本里的惰性导入——典型是字体链
tools/font_gen/make_font.py在函数体内from fontTools.ttLib import TTCollection。它会不会被走到静态判不了,所以scripts/ci_local.py的 L0a 预检把这类只列成候选(实测列出font-charset/generated-fresh两条经下游引入 fontTools),摊开给人看、不作判决;判决只留给「模块级 import + 裸 python + 该解释器确实没装 + 脚本不自带解释器解析」四事齐备那种必然崩。
⚠ 本节 2026-10-27 的早先版本写作「42 条里唯一需要第三方包的那条自带解释器解析」。那是我拿一条宽松 正则(连注释与字符串里的包名都算命中)审计出的保守过头的结论,条目数也因
nested-comment钩子 落地从 42 变 43。现在这条口径由 L0a 预检每次跑门禁前重算,人不再需要记这个数。
⇒ 写新钩子时二选一,别开第三条路:要么脚本只用标准库,要么在脚本内部按
「显式变量 > 当前解释器 > 仓库 .venv > PATH」自行解析并探针校验(先例与理由见
docs/门禁说明/generated-fresh.md §4)。不要把 entry 写死成某台机器的绝对路径。
5.2 动环境的失效顺序与回滚(含本笔机械化)
先把"谁依赖谁"钉住,才知道动一下会红在哪一步(下表每行都是本机实测或代码事实,不是推测):
| 被动什么 | 第一现场 | 症状 | 还能动什么 |
|---|---|---|---|
删 / 重建 .venv |
.git/hooks/pre-commit 里的 INSTALL_PYTHON 指它 |
git commit 当场起不来:钩子脚本先判 if [ -x "$INSTALL_PYTHON" ],不成立才回落到 PATH 上的 pre-commit——本机 where.exe pre-commit 报找不到,于是回落分支也是死路 |
重建 .venv 后必须 pip install -r requirements.txt(含 pre-commit),否则回落分支永远还是死路 |
只换 .venv 里的包(不删目录) |
pre-commit 本体与 language: python 钩子各自独立的缓存环境(~/.cache/pre-commit/repo*/py_env*/)互不相干 |
只影响 pre-commit 本体;改写型钩子跑在缓存环境里,看不见 .venv 的变动(实测那份缓存环境里连 identify 都没装) |
缓存环境坏了要 pre-commit clean——它会连别人正在跑的钩子环境一起重建,必须先要到静默窗口 |
| 换 PATH 首位解释器 | 那 43 条裸 python 条目 | 模块级第三方 import 一条都没有 ⇒ 普通情况下无感;但 3.12 → 3.14 这种版本跨度会改变标准库行为,是"靠巧合不炸"的敞口 | 设 DTU_PRECOMMIT_PYTHON 只钉得住 pre-commit 本体,钉不住裸 python 条目的落点——那只能靠脚本自解析 |
改 requirements.txt 的版本约束 |
别人机器上的一次 pip install -r |
本机无感,别人下次重建环境时才炸 | 这不属于"修本机环境",要改先报属主,别夹在诊断笔里 |
回滚按现场分条,别混用:
.venv被删 / 建坏:python -m venv .venv→pip install -r requirements.txt→& '.venv\Scripts\python.exe' -m pre_commit --version通 →ci_local.py --preflight-only通 → 再跑完整门禁。- 单包装坏:按修复前那次
pip freeze(放仓外,不入库)反向pip install <包>==<原版本>; 不要-U顺手把别人的依赖一起动。 - 缓存环境坏:
pre-commit clean后跑一次ci_local.py让它重建;重建期间别提交(几十秒内会拿到半装好的环境)。 - 只有全坏透才删
.venv重来;删前记下pip freeze,并记住本仓.venv基底是 Anaconda (换机复制不出来,见上面 §5.2 依赖表与 §5.3 第 5 条)。
本笔把上面这些机械化了一半:scripts/ci_local.py 新增 L0a 钩子解释器预检(--preflight-only
可单跑,也是 ci_local.py 每次的第一段),报清"钩子跑在谁身上 / 裸 python 落到谁 / 那个解释器缺什么 /
哪些只是候选",四事齐备即 exit 2 并指名到钩子。红-绿在临时假仓库上验过(同一份配置里 bad-hook 判 FAIL、
good-hook 判 OK,整体 rc = 2)。它不改环境、不装包、不写仓库,也不替代上面的修复死顺序——
它只保证一件事:这条链下次断掉时报错发生在第一秒,并且带着钩子的名字。
5.3 什么情况下才需要动环境,以及动之前要走死顺序
默认动作是不动(本节存在即结论:没有需要重装的东西)。只有出现下列具体现象才执行修复,
且必须先要到静默窗口——pre-commit 与 .venv 是全队共享运行面,中途换包会把别人的一次钩子跑打断:
- 停表:确认没有人在跑提交/门禁/台架(build 与 bench 两类锁都空闲,见
task-worktree); - 备份指针:记下
& '.venv\Scripts\python.exe' -m pip freeze > 仓外文件(不入库); - 只补不重建:优先
pip install --force-reinstall --no-deps <包>==<requirements.txt 里的版本>, 不要顺手-U(那会连带动别人的依赖);确实要整体重建才pip install -r requirements.txt, 且该文件必须保持纯 ASCII(Windows 下 pip 按代码页解码); - 自证(缺一不算修完):
& '.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; - 回滚:新装/升级出问题时按第 2 步的 freeze 反向
pip install <包>==<原版本>;别删.venv重来, 删了就把「基底是 Anaconda」这条不可复制性也一起删掉(换机复现不了,见本文 §3 与docs/环境与部署/项目部署环境要求.md)。
5.4 仍开着的账(不在本次范围,登记给对应属主)
DTU_PRECOMMIT_PYTHON本机当前未设,靠 §1.1 的优先级自动落到.venv;如果哪天 PATH 首位换了 解释器,静态门禁档就会跟着换——把它显式设成.venv是一条零成本的加固(改的是本机环境,不动仓库)。- 42 条裸
pythonentry 与两个解释器的版本差(3.14 vs 3.12):今天没有可观测的行为差,但这是 「靠巧合不炸」。要不要把 entry 统一改成显式解释器解析,属门禁属主的决定(同一族的先例见 §5.1)。 - 本机的
.venv基底是 Anaconda 发行版,python -m venv在别的机器上给出的不是同一个东西; 这条差异目前无人记录在docs/环境与部署/项目部署环境要求.md里。
变更记录
| 日期 | 变更 |
|---|---|
| 2026-10-27 | 新增 §5.2 动环境的失效顺序与回滚(原插成 §5.4,本笔按依赖表→死顺序→遗留账重排)+ §5.1 二次修正(道G task-33 修复分支):四种现场(删/换 .venv、只换包、换 PATH 首位、改 requirements 约束)逐行钉死"第一现场 / 症状 / 还能动什么",回滚按现场分条不混用;scripts/ci_local.py 同批加 L0a 钩子解释器预检(--preflight-only 单跑,也是每次门禁的第一段:报"钩子跑在谁身上 / 裸 python 落到谁 / 缺哪些包 / 哪些是惰性候选",四事齐备 exit 2 指名到钩子,红-绿在临时假仓库验过)。§5.1 那条「42 条里唯一需要第三方包的那条自带解释器解析」被更严的口径推翻:按模块级 import 判是 0 条,条目数也因 nested-comment 落地变 43;fontTools 属下游惰性导入,预检只列候选不判决。 |
| 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 阶段迁移及离线/实机验收范围。未改工具调用代码 |