【项目】UG/NX 全版本 AI 外挂（NX Copilot）。目标：一个独立安装、自动识别本机全部 NX 版本、
可自由切换、断网可用的 AI 自动编程软件。NX 内只允许存在"极薄桥接"，业务逻辑一律放外部程序。

【先读台账 + 总纲 · 冲突时以台账/总纲为准】
  必读①（30 秒读完）：D:\NX_AI\docs\10_状态台账.md —— 已做完什么、怎么验证的、当前基线、铁律。
                    凡台账里记录在案的【不要再重复实现、也不要重复验证】。
  必读②：D:\NX_AI\docs\09_TraeCN后续工作总纲.md
  它写明：16 张已完成卡、验收方已自行修复的 7 个缺陷（**不要重做**，重做会把已修好的逻辑改回去）、
  13 张待办卡的执行顺序，以及"卡片-现状差异对照表"（第 8 节）。
  本卡正文是在更早轮次写的，可能仍描述"待修"状态 —— 动手前先看总纲第 2 节与第 8 节。
  开工前先跑两条门禁确认起点干净（不通过就先报告，不要动手）：
      python D:\NX_AI\tools\check_py_syntax.py     # 期望：28 个 .py，0 错误 0 预警
      python D:\NX_AI\tests\run_eval.py            # 期望：通过 13/13，意外退化 0，漂移 0

【本机已核实的事实，禁止再猜，按这些事实写代码】
1. 本机装了 2 个 NX：E:\Program Files\Siemens\NX2212 与 E:\Program Files\Siemens\NX2412。
   注意：NX 不在 C 盘、不在 %ProgramFiles%，禁止把盘符写死成 C/D。
2. 版本探测的权威来源是注册表：HKLM\SOFTWARE\Siemens\NX 2212 与 HKLM\SOFTWARE\Siemens\NX 2412，
   值是 INSTALLDIR、UGII_BASE_DIR、LICENSESERVER、LANGUAGE。
   补充来源：Uninstall 项的 "Siemens NX 2212 / 22.12.1700.00000"、"Siemens NX 2412 / 24.12.7002.00005"。
   校验来源：<NX>\NXBIN\run_journal.exe 与 <NX>\NXBIN\ugraf.exe 是否存在。
3. 【重要·已实测】本机 NX 的 Python 是【以 DLL 形式内嵌】的，不需要独立 python.exe：
   E:\Program Files\Siemens\NX2212\NXBIN\python310.dll   (Python 3.10.6)
   E:\Program Files\Siemens\NX2412\NXBIN\python311.dll   (Python 3.11.9)
   E:\Program Files\Siemens\NX2412\NXBIN\nxpythonldr.dll  (NX 的 Python 加载器)
   E:\Program Files\Siemens\NX2412\NXBIN\python\          (含 NXOpen.pyd 与 NXOpen_*.pyd 全套)
   → 结论：Python journal 可用，且是版本无关性最好的通路。
   → 策略：批处理/自动化优先用 Python journal；需要进程内实时交互时才用 C#/VB。
   → 注意：NX2212 是 Python 3.10、NX2412 是 3.11，脚本必须同时兼容两个版本。
4. NX Open .NET 程序集在这里：
   E:\Program Files\Siemens\NX2212\NXBIN\managed\NXOpen.dll  (2212.0.0.0)
   E:\Program Files\Siemens\NX2412\NXBIN\managed\NXOpen.dll  (2412.0.0.0)
   两个版本是两套独立程序集 → 任何 NX 插件 DLL 必须按版本各编译一份，禁止一份打天下。
5. 编译环境：无 Visual Studio、无 .NET SDK。只能用 csc.exe：
   C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe
   目标框架 .NET Framework 4.8（本机已装）。
6. 环境变量现状（必须清理的残留）：
   UGII_VENDOR_DIR(用户级) = E:\Program Files\Siemens\NX2412\UGII\vendor   ← 单值，导致 NX2212 加载不到
   UGII_USER_DIR(用户级)   = C:\Users\Administrator\AppData\Local\NxCamToolkit\bridge
   UGII_LOCAL_USER_DEFAULTS(用户级) = C:\Users\Administrator\AppData\Roaming\NxCamWizard\nx_user.dpv ← 全局关闭了 NXOpen 签名警告
   UGII_BASE_DIR(机器级)   = E:\Program Files\Siemens\NX2412
   → 多版本切换必须用【进程级环境变量】，禁止再写系统/用户级单值变量。
7. 现有工程：C:\NxCamToolkit（Python/tkinter 桌面工具）、C:\NX_Plugins\NxCamWizard（NX 插件成品）、
   C:\Users\Administrator\AppData\Local\NxCamToolkit\bridge（文件信箱桥接 + NxAiBridge 插件）。
   文件信箱 IPC 协议设计是对的，保留并升级；NX 侧 Python journal 路线是错的，废弃。

【硬性约束】
- 不许修改 NX 安装目录下的官方文件（UGII\menus、ugraf.exe、custom_dirs.dat 等），只允许写 vendor/user 隔离目录。
- 不许写系统级/用户级环境变量来切换版本。
- 不许删除任何文件，除非先用 Copy-Item 生成带时间戳的 .bak 且备份成功。
- 不许联网、不许连 43.167.209.150、不许使用 n x_id 私钥、不许读取 trae-jwt-token。
- 不许启动 NX 界面（本次任务未获授权）。需要 NX 在线的验证一律写成脚本 + 说明，交给人工执行。
- 每个脚本必须有：-DryRun 开关、错误处理（不许裸 except）、执行前后日志、失败时非零退出码。

【编码规范】
- 主力语言：Python 3.12（桌面侧，路径 C:\Users\Administrator\AppData\Local\Programs\Python\Python312\python.exe）
  与 PowerShell 5.1（安装/探测/系统集成）；NX 侧用 C#（.NET Framework 4.8）。
- 所有新建工程文件放 D:\NX_AI\ 下（新建），不要污染 C:\NxCamToolkit。
- 中文注释、中文日志；文件一律 UTF-8 with BOM（PowerShell 脚本必须带 BOM，否则中文乱码）。
- 每个模块配一个可独立运行的 --self-test。

【NX Open 跨版本铁律 · 本机实测得出，必须遵守】
1. 判断几何特征只能用【几何量】：面（body.GetFaces()）、边（face.GetEdges()）、
   边长（edge.GetLength()）。实测同一零件 NX2212 与 NX2412 的面数(130)、面类型分布、
   孔径分布【完全一致】。
2. 【禁止】用 NX 对象个数做业务判断。实测同一零件 list(part.Bodies)：
   NX2212=92、NX2412=2；而特征数两版都是 2。按个数分支的逻辑必然出错。
3. 下列 API 在本机两版本上【不存在】，不要再尝试（已逐一实测）：
   face.Radius / face.GetRadius() / edge.Radius / edge.ArcRadius / edge.GetCurveData() /
   part.Faces / ufs.Modl.AskFaceData / body.GetBoundingBox()
   圆柱直径的正确取法：取面的圆边，**edge.GetLength() ÷ π = 直径**
   （实测 34.55751918948773 ÷ π = 11.0000000000，即 φ11）。
4. NX Open 集合【不支持】len()/ToArray()，只能迭代（list(coll) / sum(1 for _ in coll)）。
5. face.SolidFaceType 的 str() 给出数字 id（本机该零件：2→68面、1→43面、3→19面）；
   拿不准就先反射：`dir(obj)` 列真实名字，**不要凭文档猜名字**。
6. 导入/收敛几何（Brep 特征）没有参数化孔特征，孔径只能靠几何反算 —— 这是常态，不是特例。

【执行红线 · 最高优先级，高于本卡其它任何内容】
1. 绝对禁止执行任何带 -Apply / -Force / -Execute / -Run / -Kill / -Confirm:$false 的
   写入性或破坏性开关。你可以生成它们、可以用 -DryRun 验证它们，但【不得真正执行】。
   违反实例已记录一次：P0-05 卡片明确写了"不要执行 -Apply"，但 2026-10-16 14:07:52 实际执行了
   cleanup_residue.ps1 -Apply，清除了用户级 UGII_VENDOR_DIR 与 UGII_LOCAL_USER_DEFAULTS、
   移动了 nx_user.dpv（备份在 D:\NX_AI\backup\20261016140752\，人工已确认不还原）。
2. 任何会改动用户机器状态的命令（写环境变量、改注册表、移动或删除文件、启停服务、装软件、
   改 NX 安装目录内容）一律只写进"人工执行清单"，并说明为什么必须人工执行。
   允许你自动执行的只有三类：在 D:\NX_AI\ 下创建/修改你自己的工程文件、只读探测、-DryRun。
3. done.json 必须与事实一致。若确实执行了写入操作，必须在 notes 里逐条写明
   "已执行 X，时间 HH:MM:SS，影响 Y，备份在 Z"；不得声称"未执行"。
4. 涉及凭据（私钥、口令、token）只允许检测与列出，禁止删除、禁止联网、禁止尝试登录。
5. done.json 的 files 字段必须是纯字符串数组（["绝对路径", ...]），
   不得塞入对象或 PowerShell 的 hashtable 文本（P0-04 犯过这个错）。

【完成时必须做的交接动作】
在 D:\NX_AI\状态\<任务编号>.done.json 写入（UTF-8 无 BOM）：
{"task":"<任务编号>","status":"done|partial|failed","files":["产出文件绝对路径"],
 "verify":"<我该怎么验收，给出可直接复制的命令>","blocked":"<阻塞点或空>","notes":"<关键决策>"}
并且回复里用一句话汇总：做了什么、改了哪些文件、验收命令是什么。

【任务 P0-03】版本切换器：只改子进程环境，绝不改全局

背景：你之前的做法是写系统级/用户级 UGII_VENDOR_DIR（单值），导致装第二个版本时第一个失效，
本机现在就是这个状态。草莓 CM 外挂的正确做法是：每个版本一个启动批处理，在里面 set 环境变量后
再拉起该版本 NX（进程级，互不干扰）。我们要做成软件，不能靠一堆 .bat。

请完成：
1. 写 D:\NX_AI\core\nx_env.ps1，导出：
   - Get-NxEnvironment -NxRoot <路径> [-UserDir <路径>] [-CamPostDir <路径>]
     返回一个 hashtable，含该版本启动所需的全部变量（UGII_BASE_DIR、UGII_ROOT_DIR、UGII_USER_DIR、
     UGII_CUSTOM_DIRECTORY_FILE(可选)、UGII_CAM_POST_DIR、PATH 前缀=NXBIN 与 UGII）
   - Invoke-WithNxEnvironment -Env <hashtable> -ScriptBlock <{}> ：在【当前进程临时】设置、
     执行、并无条件恢复原值（必须用 try/finally，恢复包括"原本不存在该变量"的情况）
2. 写 D:\NX_AI\core\nx_launch.ps1：
   - 参数：-Version <2212|2412|latest>、-Mode <gui|journal>、-Journal <脚本>、-DryRun、-TimeoutSec
   - gui 模式：用干净环境 + Start-Process 拉起 <NX>\NXBIN\ugraf.exe，并可注入用户级定制目录
   - journal 模式：调用 P0-01 的 run_journal_probe.ps1
   - 启动前必须检查该版本 NX 是否已在运行（tasklist），已运行则给出清晰提示与分支处理
3. 关键约束：脚本执行完毕后，用 `[Environment]::GetEnvironmentVariable('UGII_VENDOR_DIR','User')` 等
   逐一比对，确认【没有任何全局变量被改动】。把这个自检写成 -AssertNoGlobalChange 开关，默认开启。
4. 写 D:\NX_AI\docs\P0-03_环境构造说明.md：解释为什么必须进程级、全局变量会造成什么故障
   （用本机 UGII_VENDOR_DIR 单值指向 NX2412 导致 NX2212 加载不到 vendor 的真实案例说明）。

验收标准：-DryRun 能打印出完整的、指向正确版本的环境变量快照；-AssertNoGlobalChange 自检通过；
切换两次（2212→2412→2212）后全局变量与初始值完全一致。
