Skip to content

Releases: sixtysevenlf/dsh-blender-plugin

v1.0.5 —— issue #14:悬垂默认排除贴床面(+ 两个各向异性缩放的旧账 + Windows 上 npm test 的 11 处死链)

Choose a tag to compare

@sixtysevenlf sixtysevenlf released this 07 Oct 13:28

v1.0.5(2026-10-07)—— issue #14:悬垂默认不再把「贴床底面」算成需要支撑(+ 两个各向异性缩放的旧账 + npm test 在 Windows 上跑不到一半)

修 1(P1 · issue #14):print_overhang / print_report 的贴床面口径

  • 症状:FDM 零件绝大多数是平底,而旧口径把「法线朝下的面」一律算悬垂 ⇒ 平底零件默认 overhang_ok=false。
    报告人的 PLA 支架 overhang_area_mm2=5351.57,其中 97%(5198.49mm²)是 z=0 的贴床平底;
    1 单位立方体 overhang_ratio=0.1667、ok=false。
  • 根因:判据只看法线(c >= cos_lim),从没问「这个面是不是贴在打印平台上」;而回执 note 却让用户
    「底面贴床的平面请用 min_area_mm2 排除」—— 与实现矛盾(那是面积预算,抬高预算会把真悬垂一并放行)。
  • 改法:新增 exclude_bed=True(默认)、bed_tol_mm=0.2、bed_mode=flat|min、bed_horiz_deg=1.0、bed_band_mm=1.0。
    贴床面 = 水平朝下(≤bed_horiz_deg)且标高落在平台面 ±bed_tol_mm 内;平台面 flat(默认)取
    「面积最大的水平朝下层」的标高、min 取所选集合最低点。回执:overhang_area_mm2 = 净悬垂、
    bed_area_mm2 单列贴床面、overhang_area_incl_bed_mm2 保留老口径、bed_level_mm/lowest_mm 可复盘;
    exclude_bed=False 整体回退 v1.0.4 老口径(净悬垂/占比/ok 逐值等价)。
  • 顺手堵掉一个假绿:flat 若无条件信任「面积最大的水平朝下面」,蘑菇件(细柱 + 宽帽)会把帽底
    当平台面 ⇒ 实测 200×200 帽底 40000mm² 真实悬垂被整块吞掉、ok=true。加 bed_band_mm 防线:候选层
    离最低点超过 1mm 就判定「它不像平台面」→ 退回 min 口径并写 bed_warning。修后同一几何:
    bed_mode=min-fallback、帽底 40000mm² 照算、ok=false。
  • 不确定不许读成绿:print_report 顶层新增 warnings[](平台面判据的自证警告,rows[].bed_warning 同步),
    gate.py 的 print preset 把 len(warnings) > 0 接进 degrade_if ⇒ 这类回执在门里是 degraded。
  • 已知边界(写进 print_help 的 limits):bed_band_mm 以内的差值判不出来 —— 「距台 ≤1mm 的宽面」与
    「底缘碎面」是同一形状,flat 一律算贴床(FDM 能桥接);另 flat/min 都几何自推、位置无关,
    整件悬空时最低点就是它自己,要按绝对平台判请先落台或用 exclude_bed=False。
  • 字段契约变更:PRINT_VERSION 1 → 2;overhang_area_mm2 语义翻转(默认不再含贴床面)。
  • 自检:print_selftest 从 1 个形状扩到 8 个(平底立方体 / 悬臂 / 平底碎块 / 蘑菇帽 / 斜面不许当贴床 /
    单件 T 的比值 / 非均匀缩放的面积真值与法线),期望值全是解析解而非实现回抄;并接进真机 A1–A5 套件。

修 2(旧账 · v1.0.4 就有):各向异性缩放下法线与面积都算错

  • 法线:M.to_3x3() @ n 应为逆转置 ((M⁻¹)ᵀ @ n)。scale(2,1,1) 下真法线离正下方 16°(必须支撑)被算成
    49° ⇒ 整块面丢出悬垂集(实测修前该斜面悬垂 0.00mm²、修后 0.34mm² 计入)。
  • 面积:p.area × (_scale_avg × mmu)² 在 scale(2,1,1) 下偏 -11.1%(1.777e6 vs 真值 2e6 mm²);
    改为按世界坐标三角扇累加(旋转/等比缩放下与原值等价,只有各向异性缩放会变)。

修 3(issue #10 的跟进项):npm test 在 Windows 上跑不到一半

  • import(path.join(...)) 得到的 Windows 盘符路径(C:\…)会被 ESM 当成 scheme c: ⇒
    ERR_UNSUPPORTED_ESM_URL_SCHEME(任何平台都可复现,不只 Windows)。11 处统一改成
    pathToFileURL(...).href:capability_lock / single_source / guidance / shape_search / backend_probe /
    route_contract / mac_selftest / sync_counts / dsh_api_compat_probe(两处内联脚本)/
    schema_resolve_selftest(父进程传 URL + 子进程自转 URL)。
  • tests/schema_resolve_selftest.mjs 新增 E 组:Windows 风格裸路径不得再撞 scheme 错 —— 这条任何平台都能跑,
    所以 Windows 的失效模式在 Linux CI 上就有回归保护。
  • tests/protocol_selftest.mjs 的「死端口」原为 flatPort + 4242:OS 动态端口在 49152–65535,> 61293 时越界 ⇒
    Socket.connect() 抛 ERR_SOCKET_BAD_PORT、那句断言根本跑不到(约 26% 概率间歇红)。改为 bind(:0) → close
    取真死端口,并把原来那条恒真断言换成「死端口 ≠ 任一 mock 端口」。
  • tests/README.md 的 preload 示例漏了 pipeline(实测红 2 条:t_pipeline / t_refs_single_op —— 是示例漏项、
    不是插件缺陷),已补全;断言数订正为实测值。

验收(全部在最终改动之后实测)

门 结果
npm test(16 步) EXIT=0
print_selftest(真机 Blender 5.2.2) ok=true · 8 形状
gate_selftest ok=true(新增真机用例:warnings 非空 ⇒ 该门 degraded)
A1–A5 集成自检(真机无头) 128 / 0 失败
gate 复审回归(真机无头) 52 / 0 失败
issue #14 复现(默认 Cube) 修前 4000000 / 0.1667 / ok=false → 修后 0 / 0 / ok=true;exclude_bed=False 仍复现老数字
变异测试(故意改坏实现) 忽略 bed_horiz_deg → 红;overhang_ratio 分母写错 → 红;法线退回 M@n → 红;面积退回 _scale_avg → 红;拆掉 bed_band_mm → 红

安装 / 升级

npm i -g @dsh-external/dsh-blender-plugin      # 或下附件里的 tgz
# 升级后**重启 DSH**(插件是启动时加载的模块);只改了后端才用 blender_viewport(op="restart")

验收判据不变:blender_viewport(op="doctor") 必须回 kind=ok,否则就是没装好(见 README §9 / README.zh-CN §8)。

字段契约变更(升级前请看一眼):print_overhang / print_report 的 overhang_area_mm2 语义翻转 ——
默认不再包含贴床面;老口径在 overhang_area_incl_bed_mm2,整体回退用 exclude_bed=False。

v1.0.4 —— 原生 Linux 成为一等平台(issue #11)

Choose a tag to compare

@sixtysevenlf sixtysevenlf released this 04 Oct 19:15

v1.0.4 —— 原生 Linux 成为一等平台(修 issue #11:取帧恒报 frame http 502)

用法:GitHub → Releases → Draft a new release → 选已存在的 tag v1.0.4 → 标题 v1.0.4 → 正文直接粘下面这段。
(本机没装 gh、也没有 GITHUB_TOKEN,所以 Release 页留给你一键建;tag 已经推上去了。)

修(issue #11,报告人 @mlfc666,Manjaro 原生 Linux)

  • 症状:原生 Linux(Blender 与宿主同机)上 blender_rt_see 恒报 Error: frame http 502,/frame.png 回
    ENOENT … /home/<u>/.dsh-blender-rt/dsh_live_viewport.png;而帧 PNG 确实写出来了,只是落在一个
    字面量反斜杠目录 /home/<u>/\wsl.localhost/Ubuntu/home/<u>/.dsh-blender-rt/ 里。旁证:/doctor 在 Manjaro 上把 distro 报成 Ubuntu。

  • 根因:runtime/config.mjs 的 wslToWin() 只分了 IS_MAC / IS_WIN,其余一律当成「Linux 宿主 ⇒ WSL +
    Windows 上的 Blender」
    ,把宿主绝对路径映射成 \\wsl.localhost\<distro>\…。同机的 Blender 会把这个串当
    相对路径,于是"Node 侧写、Blender 侧读"的文件交接全线错位(帧 / 无头脚本 / 台账 / results / trajectory /
    launch 的 statusFile / staging 都走它)。macOS 当年踩过同一个坑并修了,原生 Linux 漏了。

  • 修法:新增 IS_WSL 判据(环境变量优先 → 退回 /proc/version;systemd 服务里 dsh 不继承 WSL_DISTRO_NAME,
    见 #6,所以不能只看环境变量),wslToWin() 在非 WSL 上恒等返回;判据剥成纯函数 detectWsl(platform, env, procVersion)
    以便在任何平台上对拍。顺带两处同源问题:/doctor 的 distro 在原生 Linux 上读 /etc/os-release 报真实发行版;
    PATH_GUARD 在原生 Linux 关闭(否则每条正常脚本都回一条 render_filepath_posix 假警告)。

  • 实测对照(在 WSL 里用私有挂载命名空间把 /proc/version 绑成非 microsoft 的内核串 + 清掉 WSL_DISTRO_NAME/WSL_INTEROP,
    真走到"原生 Linux"分支;同一台机器上跑修前 / 修后两份代码对拍):

    探针 修前 修后
    wslToWin('/home/u/.dsh-blender-rt') \\wsl.localhost\Ubuntu\home\u\.dsh-blender-rt /home/u/.dsh-blender-rt
    CFG.workDirWin \\wsl.localhost\Ubuntu\tmp\dsh-blender-rt /tmp/dsh-blender-rt(与 workDirWsl 同构)
    PATHS.livePngWin \\wsl.localhost\Ubuntu\tmp\dsh-blender-rt/dsh_live_viewport.png /tmp/dsh-blender-rt/dsh_live_viewport.png
    describeConfig().distro Ubuntu Ubuntu 24.04.4 LTS(读 os-release)
    PATH_GUARD 非空(假警告) ''
  • 回归门:新增 tests/linux_selftest.mjs(npm run test:linux,25 断言,已挂进 npm test)。同一份自检打在
    修前代码上是 11 红 / 16,修后 25 全绿。

  • WSL / Windows / macOS 三条既有路径逐字节不变:wslToWin('/home/x/f.png') === '\\wsl.localhost\Ubuntu\home\x\f.png'、
    /mnt/d/… → D:\…;真机上 lease_stale_selftest 仍能拉起 Windows 版 blender.exe 并拿回 resultJson.ok=true,
    npm test 与修前逐条一致、无新增失败。

已知未修(与 issue #11 报告人的附注一致,留给后续)

  • wslPathShared() 在原生 Linux 上仍按 WSL 判据(/tmp、独立挂载一律判"不共享")⇒ /doctor 的 workDir.shared=false、
    无头脚本走"替换 / 降级"分支并带 WORKDIR_NOT_SHARED 提示。功能不挡事(同机 Blender 读得到),但语义要重新表述;
    workdir_shared_selftest 的 A1/A2/A3 把 WSL 语义写成了硬断言,得跟着一起改。

安装:npm i -g @dsh-external/dsh-blender-plugin@1.0.4(或按 README §3 五步安装)。原生 Linux 上装好后
blender_viewport op=doctor 的 distro 会报你的发行版,blender_rt_see(max_size=560) 应回帧而不是 502。

v1.0.3 — 内环从「存在」到「真能用」:修 ns 未绑定 + 声明式 shape_search

Choose a tag to compare

@sixtysevenlf sixtysevenlf released this 04 Oct 17:56

v1.0.3(2026-10-05)—— 内环从"存在"到"真能用":修 ns 未绑定(内环 6 个版本跑不通一次迭代)+ 新 op shape_search(声明式搜索)+ 描述改任务型触发词

修(P0 · 这才是"没人用 rt_loop"的真正原因):step/measure 里 ns[...] 一跑就 NameError

  • 症状:blender_rt_loop(op="start", …) 回 start_ok: true,但第一个 tick 就 error 收工 ——
    stop_reason="error" / i=0 / best=null / board_n=0,error 尾部是
    File "<dsh_loop.step>", line 1, in <module>\nNameError: name 'ns' is not defined. Did you mean: 'np'?
  • 根因:runner.py 的 exec(step_code, ns) 用 ns 当 globals,但从没把 ns 这个名字绑进命名空间
    (git log -S 'ns["ns"]' 在 v0.4.0→v1.0.2 全史无命中 ⇒ 自发布起就如此)。而 README / cookbook / 工具描述 /
    全部食谱一律写 ns["params"] / ns["score"] —— 谁照文档写谁炸,于是"内环存在但没人用得上"。
  • 修法:注入 ns["ns"] = ns 与 ns["st"] = st(后者也是文档 preloaded 列表里承诺过的名字)。
  • 实测对照(真 Blender 无头,手动驱动 tick;无头下 timers 不触发):
    修复前 run-muu3oj9hlcjh 修复后 run-muu3ppkr6508
    stop_reason error iterations
    i 0 5
    best null {score: 0.0687…}
    board_n 0 3
    ns_bound false true
  • 回归门:dsh_loop_selftest 从"只读 status/help、不真起循环"升级为真起一个最小循环并手动驱动 4 个 tick,
    硬判 ns_bound / iters_done / best_present / no_error(旧自检的结构性盲区正是这条回归活了 6 个版本的原因)。

改 1:blender_rt_loop 描述从「次数门槛」改成「任务形状」

  • 触发词:对齐 / 拟合 / 反推尺寸 / 参数扫描 —— 判据能写成一个数(轮廓 IoU、包围盒尺寸误差、剖面差,或自写 score)。
  • 明写硬前提:只在 GUI 会话有效;op=board 看候选;op=export 落可复现脚本;收敛后换通路复核;跑完场景停在最后一次迭代而非 best。
  • 长尾(spec 字段 / K.dsh_measure 三签名 / 可照抄骨架 / 4 条纪律)搬进 catalog 详情:blender_rt_plan(op="catalog", args={tool:"rt_loop"})(1,834 字符,按需取)。
  • 描述预算门仍绿:合计 11,672 ≤ 12,000,rt_loop 1,261 < 1,600。
  • 旧文案里的 ≥20 次才用 是"自我否决":模型读完就跳过 —— 换成任务型触发词后,§0 路由表 + 描述 + catalog 三级都能命中。

新 op:blender_rt_plan(op="shape_search", args={…}) —— 声明式搜索

  • 你只给目标与区间,机器去搜:objective(silhouette_iou / profile_err / aabb_err)+ params
    ({dy_mm:{min:-30,max:30,step:5}} 或 {values:[…]})+ apply(每轮把 ns["params"] 落到场景的代码;
    建议在 setup 里定义 apply(p) —— 与 op=export 的复现口径一致)。
  • 内部复用 runner:start → 轮询 → board → export 全程托管 ⇒ 只在 GUI 会话有效;
    dry_run:true 只校验并回 spec(不需要 Blender 连着)。
  • 采样 strategy:"auto":组合数 ≤ iterations 走穷举网格(迭代数自动收敛到组合数),否则随机搜索;
    可显式 grid / random;显式网格 >20000 组合直接拒绝并给骨架。
  • 收尾:内环结束时场景停在最后一次迭代的参数上,本 op 默认再执行一次 setup + apply(best) 把场景落到 best(restore:"none" 可关)。
  • 只读性:会通过 apply 改场景 ⇒ 归写 op(不在 /plan 只读白名单,受写租约约束)。
  • 回执:spec_echo + best + board + stop_reason + applied + exported + next_steps;任何校验失败都给 skeleton。
  • 代码块缩进归一化(normBody):apply/setup 支持"首行顶格 + 续行缩进"写法与嵌套块(if/else),
    不会生成 IndentationError —— 由测试里"用 python3 真编译三段代码"这条断言守住。

配套 skill(dsh-skill-blender-modeling,另一仓库,同步发布)

  • §0 路由表补 rt_loop 行;§0.7 增第四条硬规则(可标量化 + 要试几十~几千组 ⇒ 不许手工拍参数);
    新增 Recipe 24(可复制 spec + 现成目标函数表 + 5 条纪律 + 3 种不该用的场景)+ 声明式 shape_search 的用法与分工;
    front-matter 描述补"参数搜索内环"、配方计数 23 → 24。

测试

  • 新增 tests/shape_search_selftest.mjs(24 项):目录面 / spec 生成(grid · random · aabb_err · 多视平均 · pyLit 布尔)/
    python3 真编译三段代码(含嵌套块) / 校验面(缺参 · 非法目标 · 组合爆炸都必须给骨架)/ 真路由(隔离后端 POST /plan)。
  • 能力锁更新为 28 family / 184 op(+1 op,原因即本节);npm test 16 步全绿。

v1.0.0 —— 能力冻结:15 工具 · 28 family / 183 op

Choose a tag to compare

@sixtysevenlf sixtysevenlf released this 27 Sep 18:34

DSH × Blender 直连实时插件 · 分享版 v1.0.0

1.0 = 能力冻结线:15 个模型侧工具(看视口 / 跑 Python / 时间窗观察 / Blender 侧内环 / addon 命令透传 / 渲染性能 / 对象精简 / 无头进程 / 热无头 / 事务回滚 / 配方库 / 作业层 / 契约层与规划器 / 通道运维)+ blender_rt_plan 的 28 个 family · 183 个 op(判定 · 验收 · 导出 · 造型)。

📦 npm 包已打好:dsh-external-dsh-blender-plugin-1.0.0.tgz(1.3 MB / 125 文件,见下方 Assets)。
发布到 registry 需维护者的 npm 账号(本机尚无 npm 登录),维护者执行一次即可:

npm login
npm publish dsh-external-dsh-blender-plugin-1.0.0.tgz --access public

安装三步与唯一验收判据(doctor 回 kind=ok)见 README ·
配套 skill:sixtysevenlf/dsh-skill-blender-modeling


v1.0.0(2026-09-28)—— 首个 1.0 发布:macOS 全 GUI 路径打通 + 对外能力冻结

1.0 = 能力冻结线:15 个模型侧工具 + blender_rt_plan(28 个 family / 183 个 op)是对外稳定面;
包版本(package.json)、工具描述前缀([v1.0.0] …)、npm 包名 @dsh-external/dsh-blender-plugin 三者一致。
相对 v0.9.6 的功能增量只有一项:macOS 支持(见下);其余为既有能力收口 + README(中英)重写为「只写 1.0 现状」,
不再按版本号罗列历史 —— 历史留在本文件里。

仓库同一提交的另两个动作:
① 删除本地与远端分支 backup/v0.9.6-local-0926-2005(独有 commit 0f99949,删除后不再有 ref 指向它;
本地回看用 git show 0f99949,远端若已被 GC 则取不回)—— 此后只保留并推送 main;
② 版本号 0.9.6 → 1.0.0(package.json / src/index.ts / lib/index.js 的 PLUGIN_VERSION),并重建 lib/。

macOS 上宿主与 Blender 同机同 OS,跨 OS 路径改写退化为恒等。
原先这些改写会把 /Users/... 拼成 \\wsl.localhost\Ubuntu\Users\... —— Blender 静默打不开。
Windows / WSL 行为不变(有回归断言兜底)。

新增

  • runtime/config.mjs:IS_MAC;winToWsl / wslToWin 在 mac 上早退返回原值;
    detectBlenderExe() 增扫 .app bundle(/Applications、~/Applications、/Volumes/*/Applications,
    支持任意版本名 Blender*.app);resolveWorkDir() 改用 ~/.dsh-blender-rt
    (不落系统临时目录 —— 那里会被清理,帧与台账会丢);winLocalAppData() 不再尝试调 cmd.exe。
  • runtime/engine.mjs:blenderJoin() 收编原先散落的 path.win32.join(mac 上会产出 \Users\...);
    withWslEnv() 同 OS 时直接返回(不再往子进程塞 WSLENV);注入 Blender 的 python 路径助手
    (_dsh_slashes / _dsh_win_path / _dsh_wsl_path)带 __DSH_NATIVE_POSIX__ 渲染开关,mac 上恒等;
    PATH_GUARD(WSL 专属的 render.filepath 体检)在 mac 上关闭,避免把正常的 /Users/... 误报成路径问题。
  • src/index.ts:killBackendByCmd() 在没有 /proc 时回退 ps -ax -o pid=,command=
    (macOS 无 /proc,原先静默返回 [],热重载后残留后端停不掉)。
  • tests/mac_selftest.mjs(npm run test:mac):45 条断言,纯 Node + 真跑一次 python3
    执行注入的路径助手;含回归保护(断言 WSL/Windows 分支仍在,防止 mac 支持把原逻辑吃掉)。

修复

  • runtime/config.mjs:PATHS.livePngWin / PATHS.viewPngWin 硬编码 path.win32.join →
    在 mac 上产出 \Users\...,取视口截图会静默失败(该路径会被内联进 bpy 脚本、也直接交给 addon)。
  • runtime/qc.py:「已是绝对路径」的判据补上 / 开头(原来只认 X: 与 \\)。

实机验证(macOS arm64 + Blender 5.2.2 LTS + MCP for Blender 1.7)

路径层(npm run test:mac,45 断言,纯 Node + 真跑 python3):

  • 无头运行 status: finished / 退出码 0 / workdir·runId·env 契约正确;
  • 真实渲染产出合法 160x120 RGBA PNG;
  • GPUOffScreen 自定义视角自检 gpu_init: ok、first_px == [25,153,51,255]、投影误差 ≈1e-7
    (与 Windows 参考值一致)。

GUI 通道(npm run test:gui,需真机 + addon)—— 11/11 通过:

  • blender_viewport op=doctor / op=status:通道健康,读出真实 3D 视口 region;
  • blender_rt_see:离屏取帧 148ms,合法 RGBA PNG,写入 ~/.dsh-blender-rt;
  • blender_rt_see {from, look_at}:自定义视角 178ms,覆盖 26.8%,用户视口未被改动;
  • blender_rt_do:改写场景 + 读回验证通过;持久内核 K 跨调用保留;
  • blender_rt_cmd / blender_rt_commands / blender_rt_perf / blender_rt_opt 均正常;
  • blender_viewport op=launch 冷启动:2.5s 从零到一个已 Connect 的 GUI Blender(原先需人手点面板)。

npm test 既有回归(协议 / 租约)不受影响。

v0.9.4 — 返回通道修复(P0)+ 一键拉起 Blender + 死会话租约回收

Choose a tag to compare

@sixtysevenlf sixtysevenlf released this 25 Sep 05:34

发布:2026-09-25 · 仓库 sixtysevenlf/dsh-blender-plugin · commit f702c87 · tag v0.9.4
资产:dsh-blender-plugin-0.9.4-shareable.tgz(package/ 根,72 文件)/ .zip(根目录平铺,同 0.9.1 口径)
对应反馈:另一会话《M1A1 分件建模》体感清单(500 对象 / 7 个 agent / 全程 headless)—— 逐条回应见 docs/feedback/改进落地记录-2026-09-25.md
配套 skill:**https://github.com/sixtysevenlf/dsh-skill-blender-modeling**(blender-modeling v2:多 Agent 分工范式 / 参考图形体还原 / 六条坑 / 数值门装配审计)


v0.9.4(2026-09-25)—— 返回通道修复(P0)+ 一键拉起 Blender + 死会话租约回收

P0 · blender_rt_headless 每次都失败、rt_job 收不回结果

现象(反馈原文):blender_rt_headless 每次都返回 value is not lossless JSON(三种参数组合都不行),
rt_job(op=start) 能提交但 status/collect/wait 同样失败 ——「任务能交出去但收不回来」,
只能自己去 jobs/<id>/stdout.log 里捞 HEADLESS {…}。反馈当时的猜测是"harness 拒绝非有限数"。

真因(已在本机复现 + 定位到行):v0.9.3 的 receiptEnvelope() 里有

promoted: ctx.mode === 'promoted' ? true : undefined,

宿主 DSH 用 dsh-util-values 的 walkJsonValue 校验工具返回值,规则比 JSON.stringify 严格得多:
值为 undefined 的自有可枚举键会被拒收(而 JSON.stringify 会把这个键丢掉 —— 所以日志、单测、
肉眼全都看不出来)。而 receiptEnvelope 正是 headless(sync) / headless(job 完成) /
rt_job 的 status·collect·wait 的共同出口 —— 与"op=start 能交、其余全挂"的现象逐条对上。
控制组实测:{promoted: undefined} → 拒收;{promoted: null} → 通过。
回归测试抓到同类第二处:gpu.fell_back_to_cpu(后端没给该字段时也是 undefined)。

修法(三层)

  1. envelope 改「有才给键」:...(ctx.mode === 'promoted' ? { promoted: true } : {});
  2. gpu.fell_back_to_cpu: !!r.gpu.fell_back_to_cpu;
  3. 所有工具出口统一 losslessSanitize()(vTool 是唯一出口):undefined→丢键(数组元素→null,长度不塌)·
    NaN/±Infinity→null · -0→0 · bigint→number/string · Date→ISO · Map/Set→数组 · 类实例→自有可枚举键 ·
    二进制→{bytes:N} · 循环→[Circular]。改动不静默:逐处记进 fixes 并追加到回执文本
    (⚠️ 回执已消毒 N 处…)+ console.warn。

P1 · blender_viewport(op="launch"):agent 自己点亮 GUI

反馈原文:「文档写的是按 N → MCP for Blender → Connect,但 agent 没有人手」。
这条把"启动 GUI Blender + 让 addon 起 socket server"固化成一个调用:写 boot 脚本(GUI 里 addon_utils.enable
名字带 mcp 的模块;不行就 importlib 直接载入 $BLENDER_USER_SCRIPTS/addons/*mcp*.py + register())→
detached spawn blender.exe → 轮询 9876 端口(唯一可信判据,不靠"进程活着")→ 端口开了顺手 doctor。

  • 幂等:已在监听 → already:true,不会堆出第二个 Blender;
  • 支持 wait_ms / file / exe / addon_module / addon_file / dry_run;
  • boot 结论落盘 launch-status.json(GUI 进程 stdout 拿不到,只能让它自己写),回执给 bootScript 可手工复跑。

P1+ · 死会话的写租约自动回收(实测踩到)

另一个 DSH 会话崩掉/被关掉之后,它的写租约仍留在后端内存里继续生效到 TTL(默认 1 h),
把别人的写通道整段挡死 —— 而那个会话早已不存在(本机实测:holder 进程不存在,仍剩 57 min)。
现在:holder 约定 plugin-pid-<pid> → 后端在写请求到达时与 op=lease 时按 process.kill(pid, 0) 探活,
判死即回收并记录(不静默)。三态:holderAlive = true / false / null(判不了 → 保守继续挡,绝不误抢)。

回归(全绿,均可复跑)

套件 结果
node tests/lossless_guard.mjs(新,用宿主真校验器) 40 通过 / 0 失败
node tests/lossless_guard.mjs --live(真跑 headless + job status 再验) 47 通过 / 0 失败
node tests/launch_selftest.mjs(新,假 blender) 24 通过 / 0 失败
node tests/lease_stale_selftest.mjs(新) 16 通过 / 0 失败
node tests/acceptance_v093.mjs(v0.9.3 全部验收,查回归) 62 通过 / 0 失败
node tests/protocol_selftest.mjs 28 项全通过

⚠ 升级注意

  • 插件是 DSH 启动时加载的模块:升级后要重启 DSH 才会在当前会话生效(实测:不重启时本会话仍报同一条 lossless 错误);
  • 只改了后端 runtime 的部分(op=launch / 租约回收)只需 blender_viewport op=restart 即可生效;
  • 无需任何迁移动作;配置与既有调用方式不变。

v0.9.1 — rifle-build《93 反馈》修复:静默失败 ×4 / API 一致性 / 文件级算子 / GUI 原语 / 渲染队列

Choose a tag to compare

@sixtysevenlf sixtysevenlf released this 17 Sep 16:34

发布:2026-09-18 · 仓库 sixtysevenlf/dsh-blender-plugin · commit dfee1de · tag v0.9.1
资产:dsh-blender-plugin-0.9.1-shareable.tgz / .zip(package/ 根,60 文件)
对应反馈:rifle-build《93 · DSH Blender 插件使用反馈(全员汇总)》14 条 —— 逐条回应见 share/github/反馈回应-rifle-build-93.md


v0.9.1(2026-09-18)—— 修 rifle-build《93 反馈》:静默失败(P0)×4 + API 一致性 + 文件级算子 + GUI 原语 + 渲染队列

来源:rifle-build(QBZ47-5.8 影视级建模)全员 14 条实测反馈(lead + 5 名子代理,逐条带现象/复现/证据路径)。
逐条回代码核实 + 实证复现:A1/A2/A3/B3 四条已复现;D2(自定义视角回空帧)未能复现 —— 那是瞄空
(from/look_at 与几何不在同一处:我用正确坐标时画面正中就是模型),但这恰恰说明它不该"静默给一张空图",
本版给它加了自诊断(见 D2)。

P0-A1 未知名视角不再静默替换(qc_render v3)

  • views 非空且一个都没解析成功 → ok:false + unknown_views + available_views(22 名目录)+ hint;
    一张都不渲(旧行为:静默兜底渲 4 视角且 ok:true → "文件名对、角度错"的图混进证据链)。
  • views 省略/空 → 与 v0.8.11 一致的默认 3 视角(iso/front/right)。要 Procedura 的 4 视角集就显式传那四个名字
    —— 补丁版不静默改默认产物集。
  • 返回体新增 requested_count / rendered_count / unknown_count(成员踩过"12 个名字只渲 8 张不报错")。
  • 补拼写类别名:side_left/left_side/left_view → left、side_right/right_side/right_view → right、front_view → front、
    back_view → back、top_view/topdown/top_down → top、bottom_view → bottom、iso_left → iso_l、
    iso_right/isoright/iso_back_right → iso_br;语义模糊的一律不猜(muzzle_end/breech_end/*_end 仍进 unknown_views)。
    实测:["front","totally_bogus","side_left"] → 3/2/1,实际渲 front+left。

P0-A2 无头 QC 默认 view_transform="Standard"(qc_render v3)

  • 缺省 = "Standard"(去掉 AgX/Filmic 洗淡,阈值判读才可靠);传 "scene" = 沿用场景(旧行为,beauty 图用);传具体名 = 用那个。
  • 返回体与每行 jsonl 都带实际用的 view_transform;出图后逐项还原场景(实测:缺省返回 Standard 而场景仍是 AgX)。
  • ⚠ 行为变更:依赖"跟随场景色彩变换"的老调用请显式传 view_transform="scene"。

P0-A3 结构化结果不再只能从 stdout 切片

  • blender_rt_headless 新增 out_json=<path>;>4KB 的结果自动落到工作目录 results/<runId>.json(返回体 resultPath/resultBytes)。
  • 工具文本里 result 显示上限 2 KB → 6 KB;已有结构化结果时 stdout 块只留头尾(不再重复放大)。
  • plan 通道结果上限 4 KB → 12 KB(量测表/探针输出不再被砍)。

P0/P1-A4 假失败分档 + 按 runId 回收

  • 返回体新增 status(finished / script_error / blender_error / timeout / gpu_required_missing / blender_exe_missing)+ failure_hint。
    实测坑:脚本 raise ValueError("boom") 时 Blender 退出码仍是 0,旧判据只看 exit 会漏判 → 现在按"回执缺失 + stderr 异常标记"判 script_error。
  • runId + how_to_recover 进返回体:客户端断连不代表失败,用 blender_rt_job(op="status"/"collect", id=runId) 回收。

P1-B1/B2 API 一致性

  • K.dsh_*_api 从普通 dict 换成可调用 dict 子类:api("op", {…}) / api({…}) → 已解析的 dict;api.call(...) 同义;
    api["dispatch"](op, json_str) 仍返回 str(引擎契约不变)。覆盖 audit / contract / txn / deliver / generator / motion / planner / presets / perf / runner + qc_render + view。
  • 顺带记一个坑:dict 是静态类型,instance.__class__ = 子类 会 TypeError(实测)→ 实现改成"换成新实例"。

P1-B3 headless 收脚本文件

  • 新增 script_file=<.py>(Windows D:\… 与 WSL /home/… 都收);file= 传 .py 时自动识别为脚本并标 auto_from_file:true
    (旧版直接 File format is not supported in file "…py" —— 实测复现)。

P1-B4 参数/环境契约

  • 新增 env={…} 入参;插件自动注入 DSH_RUN_ID / DSH_OUTDIR / DSH_ARGS / DSH_SESSION / DSH_PLUGIN_VERSION,
    脚本里可读 K.args / K.run_id / K.session / K.outdir_env / K.env。
  • 实测坑:从 WSL 侧 spawn Windows 的 blender.exe 时 env 不自动跨界(Node 传了 6 个 DSH_*,子进程 os.environ 一个都看不到)
    → 自动声明 WSLENV 并在脚本里再注入一次(双保险)。实测 K.args == ["delta","echo"] 且脚本内能看到 DSH_*。

P2-C1 文件级干涉/重叠算子(audit v3:AUDIT_VERSION 2→3)

  • audit_overlap(file_a, obj_a, file_b, obj_b):BVH 三角面对 + 真交线段长度 + 交叠 bbox(场景单位与 mm 都给);
    两端都可以是"当前会话对象"或"另一个 .blend 文件",不必先 register_component。
  • audit_interference(...):交集体积估计(mm³)+ 95% 置信区间 + 三态(supported / refuted / unresolved);
    口径写清:蒙特卡洛射线奇偶校验、只在交叠区域采样、不是精确布尔;开放网格/非流形 → unresolved(不给看着很准的假数)。
    实测(1×1×1 立方体 vs 沿 X 平移 0.5,解析交集体 5×10⁸ mm³):audit_overlap pair_count=8 / 交叠 bbox 与解析盒精确一致 / 真交线段 500 mm / 18 ms;
    audit_interference 496,620,000 mm³ vs 解析 500,000,000 → 相对误差 0.676%(95% CI 0.623%)→ supported;不相交 → refuted(上界 1 mm³);开放网格 → unresolved 且 volume=null。
    自检 tests/audit_file_selftest.py → 50/50。R2 自检还抓出并修掉三个真 bug:射线推进 eps 必须 > float32 几何分辨率(否则 0.92% 系统性偏差 → 体积少 10%)、
    _ray_limit 漏除方向分量(斜射线提前 1.73× 截断)、双文件加载时两侧 cleanup 互相删对方对象。
    ⚠ 误差口径修正:我 brief 里那句"n=200k、占比 10% 时约 ±0.4%"偏乐观,正确值 ±1.31%(公式已写进返回体 error_caliber)。

P2-C2 audit 类 op 支持 file=

  • audit_connectivity / audit_gate / audit_measure 都接受 file=<.blend>:临时加载 → 同一分析链路 → 无论成败都清理;
    返回体带 source: "file:<path>" 与 cleanup 明细(实测 file= 结果与会话侧 objects/tris/bbox 逐项相等,对象名保持文件原名)。

P3-D1 GUI 原语(view v2)

  • gui_frame(object=…) / gui_shading(mode=…) / gui_open(path=…) / gui_help() —— 由插件侧在真 UI 上下文执行
    (rt_do 里 bpy.context.screen 是 None)。实测 gui_frame(object="Cube") → framed:"Cube", mode:"selected"。

P3-D2 自定义视角自诊断(view v2)

  • 返回体带 coverage_estimate(底色取众数,background=true 也不会误判)与 objects_in_frame(把物体 bbox 投到画面里数)+ scene_bbox;
    数不到几何时给 warning{code:"frame_looks_empty", scene_bbox, suggest:{from,look_at,lens}}。
    实测:瞄空帧 → 覆盖 0.00% + 告警 + suggest;照 suggest 再出一次 → 覆盖 68.11%(不必自己按 Home)。
  • 顺带修:x-dsh-view 头过去只带 5 个字段且截 500 字符;非 ASCII(中文告警)直接 502 "Invalid character in header content" → 头值改 URL 编码。

P2-E1 渲染队列/锁(qc_render v3)—— 替代成员自建的 render_lock.py

  • 跨进程文件锁 <工作目录>/locks/render.lock(TTL + 持有者 + 等待毫秒):blender_rt_plan(op="render_lock", args={action:"acquire"|"release"|"status"});
    qc_render_views 渲染前后自动 acquire/release(lock=false 可关),waited_ms / holder / stale 写进返回体与每行 jsonl(计时口径可审计)。
  • holder 缺省 DSH_SESSION#<pid>(同一会话里并发多进程也彼此唯一);断锁只认 acquired_at 时间戳 + TTL,不看进程存活。
  • 实测:第二个持有者 wait_s=1 → 拒绝并回报 waited_ms=1001 + 当前 holder;错误 holder release 被拒;过期锁被打破(伪造 2 小时前的锁 + TTL 60s → stale_broken:true);
    线上真锁现场:一个被掐掉的进程留下锁 → 600s TTL 到期后被下一个 acquire 打破。

P3-E2 默认产物路径带会话

  • 会话名 DSH_SESSION(缺省 plugin-pid-<pid>):evidence 默认路径 → dsh_evidence_<session>.png(实测);status 里给 session / resultsDir。

顺带修掉的工具层真 bug(本轮实测发现)

  • plan 工具的 args 会被静默丢弃:harness 的 type:"json" 有时把对象给成字符串,旧代码 typeof x === 'object' 判空 →
    任何 plan op 的参数被无声忽略却返回 ok:true(本轮 gui_frame / gui_shading 踩到;给 bogus_key 也不报"参数不匹配")。
    现在 jsonPayload() 容错解析(对象/JSON 字符串都收),非法 JSON 明确报错。
  • args 传数组被 String() 变成 "[delta]" → 数组直传。
  • perf/opt 无参 op 传了 {} → python 侧多收一个位置参数(实测 _dsh_perf_status_cycles() takes 0 positional arguments but 1 was given)→ 空参回退 undefined。

自检与回归

  • 新增 tests/engine_probe.sh(16 项后端 HTTP 探针)→ 16/16;新增 tests/view_diag_selftest.py(16 项)→ 16/16;
    qc_render 自检 → 29/29(v3);tests/audit_file_selftest.py → 见下;既有 9 个自检全部复跑。

v0.9.0 — 装配级判据 / 数值化漂移 / 判定随几何失效(Procedura 融合)

Choose a tag to compare

@sixtysevenlf sixtysevenlf released this 16 Sep 19:24

v0.9.0(2026-09-17)—— Procedura 融合:装配级判据 + 数值化改动度量 + 判定随几何失效

来源:对 SpatiaOS/Procedura(MIT,Agentic 3D Modeling with Procedural Control)逐文件精读后的移植评估
(分析表:docs/Procedura-融合分析.md)。只搬判据与算法,不搬技术栈(不引 OpenSCAD / Isaac / 它的 TS 运行时)。
全量回归:9 个自检各自独立进程全绿(下面逐条给实测数字)。

  • ★ 先修一个真 bug:audit_* 从来没接通。docs/ 里一直文档化的 blender_rt_plan(op="audit_mesh" / "audit_scene" / "audit_duplicates")
    实际会掉进契约层报 unknown contract op(实测 audit_help 返回 "unknown contract op")—— audit.py 只在 preload 通道可达。
    本版把 audit_*、montage 正式接进引擎路由(engine.mjs + server.mjs 只读名单),并补上 motion_* / deliver_* / generator_* 三条新路由。

  • 装配级连通门 audit_connectivity / audit_gate:连通分量 + 浮块归因 + 微隙分级。
    判据:可见浮块 = 最长 bbox 边 ≥ 全模型最长边 1%;相接口径 micro_gap_mm(默认 0.3)。
    与上游两处有意分歧(都写进返回体,不藏):
    ① 不再用「最大体积分量=主体、其余都是浮块」—— 两个等大零件时那条规则会随便挑一个当浮体(自检抓到过,A/C 体积都是 1.0);
    改成每个分量都问「你有没有跟别的分量相接」(attached 三分类:floaters / tolerated / unconfirmed)。
    ② 上游靠 bbox 间隙判 micro(单网格够用;多对象装配里 bbox 普遍重叠 → 间隙恒为 0 → 一律放行),本版补网格级复核:
    BVH find_nearest_range + 面上采样(≤400 点,取顶点会漏 —— 大平面间的 0.2mm 缝顶点可能相距几米)。
    自检用例 F(bbox 与 A 重叠、面到面 1.32 mm)被判浮块;用例 C(0.2 mm 缝)判真 micro。
    大网格(>50 万三角面)跳过网格复核 → attached=None → 门降级 ok=null,未确认不得读作已通过。
    口径与分布随返回回传(gate_caliber / gap_histogram / floater_gap_range_mm)。
    真机实测(COCKPIT_DELIVERY,14 对象 / 97,438 面):0.3 mm 口径 → 178 个可见浮块(与最近邻相距 0.33–1.14 mm)+ 970 个确认相接;
    2.0 mm 口径 → 63 个浮块。说明该模型是「贴着不接触」的装配件,不是单一实体 —— 口径该给多少取决于你要什么。

  • 网格漂移度量 audit_drift:对称 Chamfer 距离 = 形状漂移(不是倒角!)。上游口径是「各自归一化 → 点到最近采样点」,
    本版换成 BVHTree 点到曲面:20k 采样 21,397 ms → 46 ms(460×),且同网格读 0.000000(上游口径的采样噪音底是 0.017)。
    ⚠ 语义:归一化会消除平移与整体缩放 → 它回答「形状改了多远」,不回答「挪了多远」;位移另由 bbox_delta.moved_mm 报(自检把这两条都钉住)。
    band:≤0.01 基本未变 / 0.01–0.05 轻微(≤最长边 2.5%)/ >0.05 明显;grid 兜底口径系统性偏大,返回里标 metric。

  • 测量包 audit_measure / 浮块贴合 audit_snap_floaters:世界 bbox + 逐轴间隙/重叠(mm 与 %)+ 邻居(契约图 + 空间最近 K);
    snap 默认只报告,且只对「整件属于该浮块」的对象平移(焊接体一块不撕 —— 上游 v3 snap 的翻车点),
    bbox 读成 0 时自动改走网格级最近点对。

  • 证据随改动失效 + 判定漂移自动降级(契约层):新增 fingerprint(对象/面数/点数/世界 bbox + 12 位 digest);
    证据与判定都绑指纹,ledger 逐条对拍标 stale;verify 记 bbox 快照,之后几何漂移 > 10% 对角线(或尺寸 > 20%)
    → supported 在 status/ledger 读取时自动降级 unresolved(带动画数据的对象跳过)。契约自检 24 → 33 项。

  • 渲染 harness 三件(qc_render v2):mode="parts_color"(12 色逐部件配色 + parts_color_meta.txt 图例 + 出图后逐项还原,
    共享 mesh 会先复制隔离,否则串色)、jsonl 每行实测 device(防 Cycles 静默回落 CPU ≈7×;拿不到就 null + 点名,不猜)、
    qc_render_catalog(22 视角 / 4 组 / 别名归一:iso-FR-top/isometric/isoright → iso;未知名不再静默当 iso 渲)。
    顺带修两个既有问题:op="qc_render_help" 过去一直落到 unknown(engine 会剥 3 个字符前缀,已补 render_* 别名);
    _set_engine 在取景失败早退时会把引擎改掉不还原。自检 14 条 + 边界 15 条全绿。

  • 配合门 + 装配特征库 + 干涉升级(契约层 v3):mate_check 在实际网格上量接触面积占比 / 单边间隙中位值 / 侵入深度
    (接触距离 = 1.5% 合并对角线;采样 ≤4000;双向面积占比取 max;fit 四档 clearance 0.25 / location 0.15 / press −0.05 / snap 0.20);
    fit_help 读 runtime/assembly_features.json(9 特征 + 四档 + 导向倒角 + ISO 273 + 共享标称纪律);
    interference_report 逐对报深度/体积占比/严重度,把已注册连接的两端标 declared(计划内接触默认豁免,超 depth_eps 默认 0.2 mm 仍报);
    check_interface 做加法升级(老 bbox 字段逐字不变 + 新增 mesh 块)。
    实测:正常配合单边中位间隙 0.1498 mm(设计 0.1500)→ supported;挪远 5 mm → refuted 且点名「完全没搭上」;
    深穿模 depth_max 3.000 mm > 0.2 → refuted;samples=2 → unresolved;过盈档实测 0.0499(设计 0.05)→ supported。
    自检 33 → 57 项。

  • 待定编辑事务 + 交付导出(txn v3 + 新模块 deliver.py):edit_begin / edit_check / edit_accept(verified) / edit_revert(why) / edit_status
    ——同一时间一个 pending;接受必须「有复核 + 一句 verified(空串拒)」;撤销免预算但有上限 max(2, 累计步数) 且防抖(同 label 连续 revert 拒);
    deliver_export / deliver_verify:单位盒归一化 + 多组 OBJ/MTL + manifest(md5/字节/单位换算/归一化三件套),
    改一字节或加一个面必须 FAIL。txn 自检 55 项、deliver 自检 53 项。

  • 轨迹 JSONL:每次顶层调用写 <工作目录>/trajectory/blender_rt-<日期>.jsonl(route/op/ms/ok/参数摘要/摘要 hash),
    超 16 MB 轮转;DSH_TRAJ=0 关、DSH_TRAJ_FULL=1 记更多参数;engine.status() 里给路径。

  • 铰接/机构 motion_*(新模块 motion.py):关节轴/锚点实测(旋转对称 + 接触带两条独立证据,冲突时报 unresolved 而不是二选一)、
    扫掠验证(替代 Isaac:多相位驱动 + BVH 干涉,零干涉且确实位移 → supported;撞上就 refuted 并点名,实测点名 MS_Blocker,最深 0.019 m)、
    URDF/USDA 导出(单树合成、退化关节降级 fixed 并报 warning、结构自检:XML 可解析 / 名字唯一 / 端点存在 / 无环 / mesh 文件在)。
    扫掠后所有对象 matrix_world 逐项对拍还原(max_delta 0.0)。自检 25/25(另一条证据通路:铰链沿 Y + 无销的接触带定轴 + prismatic 滑块)。
    过程中修掉三个真坑:① %.9f 会把 mm 级质量 8.6e-11 静默写成 0(改 %.9g 并加断言);
    ② meters_per_unit 默认 0.001 在米制场景会给出荒谬量级 → 新增 meters_per_unit="scene"(读 scale_length)+ 单位可疑警告;
    ③ 同一对象被两个关节组认领会在 URDF/USDA 里重复计入几何与质量 → 现在 ok:false 并列出 conflicts。
    边界(motion_help 里写明):是运动学+BVH 扫掠,不是物理验证;零干涉需至少一个旁观者对象,否则 unresolved;
    轴推断只对可辨识几何有效(薄平板 → unresolved);USDA 是最小可用 ASCII,不是完整 UsdPhysics schema;本机没有 URDF/USD 消费者可做端到端加载验证,只有结构自检。

  • 生成器 generator_*(新模块 generator.py)—— #18 代码化建模通道的 Blender 原生轻量版。
    本机两侧都没装 OpenSCAD(真接它要 Manifold 后端:同一 hull() CGAL 1774 s vs Manifold 1.77 s),所以把那四件事用 Blender 原生方式拿到:
    程序即形状(注册一段 python 生成器)+ 编译门(在全新无头进程里从零复现)+ 模块级缓存(源码 hash + 参数 hash 都没变就不重跑)

    • 源码一变回执过期。自检实测:3 个方块 supported(805 ms)→ 再跑命中缓存(ms=0)→ expect 不符 refuted → 没给 expect unresolved →
      改源码后 diff.changed=true/verdict=refuted → 坏脚本 refuted。

顺带修掉的两个基础设施问题(都来自本版实测):

  • preload 多模块互相覆写 helper:engine 把多个模块源码拼进同一 globals,而每个 runtime 模块都定义 _j/_kernel/_store/_now →
    实测 preload="txn,deliver" 直接把 txn 打崩(KeyError: 'marks')。现在每个模块 exec 到自己的命名空间
    (私有名不外泄,公开名与常用 import 照旧拷回 globals → 向后兼容),两个路径(headless / 作业层)都改。
  • 两条通道语义坑(已写进各自的 scope/help):① headless 的 ok:true / exit=0 不等于脚本成功 —— Blender 的 --python
    抛异常时退出码仍是 0(生成器自检抓到),判定必须看回执/异常标记;② 源码注入通道里没有 __file__(读同目录数据文件要用 K.runtime_dir 兜底,
    见 contract._features_path())。

v0.8.10 — 失败语义 / 静默失效 / 体检与拼图一等公民

Choose a tag to compare

@sixtysevenlf sixtysevenlf released this 16 Sep 14:33

v0.8.10 — 失败语义 / 静默失效 / 体检与拼图一等公民

来源:j20-build 项目五方反馈(三份报告 00/92/93)。

先确认版本:他们跑的是 v0.8.8 —— 所以"最贵失败"里的假失败系列(126 张批量渲染报 exit=undefined、
并行 headless 误报、连 partial stdout 都没有)是 v0.6.0 起就潜伏的 NDJSON 解析 bug,v0.8.9 已修
(git 级证据:v0.8.8 的 backendPost 是整段 JSON.parse,而 /headless /worker /txn /preset 每 15 s 先写一行心跳)。
本版处理其余 20 条。

A 组 · 失败语义

  • A1 未知信封兜底:信封里既没 error 也没 result 时,不再打印 exit=undefined · undefinedms 完事 —— 改为
    HTTP 状态 + 后端回的键名 + 原始响应前 2KB + 预期日志路径,并明说"结果缺字段(不是失败)"。
  • A2 超时三态 + 运行台账:每次无头运行登记成 run-*(pid / 脚本 / outdir / 日志 / 产物数 / expect);
    客户端窗口到点不再当失败 —— 回执给"服务端子进程仍在跑"+ 最近台账 + 跟进命令;
    blender_rt_job(op="status"/"collect"/"kill", id="run-…") 可直接查(与作业同一 id 空间)。
  • A3 stdout 头+尾:engine 与工具两层都从"只给尾部"改成 头 1.5KB + 尾 2.5KB + 省略标记 + 全量落盘路径,并标注"已截断"。

B 组 · 静默失效(最贵那一类)

  • B1 参数作用域强校验:顶层/view 双向白名单;"只在顶层生效的键"出现在子字典 → scope_warnings + 回显实际生效值;
    返回里增 targets_resolved。这就是"234 张假证据"的根因。
  • B2 expect 哨兵:headless(expect={"minNewObjects":1}) 前后各打一次对象/网格/材质计数 → 增量不足即 ok=false。
  • B3 「太漂亮」守卫 + 掩膜口径统一:IoU ≥0.9999 / 面积完全相同 / 零位移却 IoU≈1 → 自动告警;
    参考图侧也走 alpha 判定 —— 同一张图自比:0.565 → 1.0000(这是"同一轮廓被口径压到 0.62 假低分"的正主)。

C 组 · 能力型

项 内容 实测
C1 网格体检 blender_rt_plan(op="audit_scene"/"audit_mesh"/"audit_duplicates"/"audit_purge_orphans"):非流形/边界边/退化面/松散点/BVHTree 自交/法线朝向/闭合/AABB/越界;0 mesh ⇒ ok=false 两个重叠立方体 → 5 对相交面;开放网格 boundary=8、clean=false;没有的对象 → 硬失败
C2 计时 harness qc_render_views 增 warmup(编译帧不计时/不占预算/不进 jsonl)、ladder+per_view_budget_s(自动降档+逐级留痕)、engine_check 回读 预热 1377ms 被排除,真实单张 283ms → 降档 104ms(256²,step=1)
C3 旋转对齐 qc_align_rotate:scale/shift 之后 ±8° 粗扫 + ±1° 细扫 L 形轮廓转 5° → 找回 -4°,IoU 0.8376 → 0.9185(+0.0809)
C4 拼图 runtime/montage.py:接触表 + 网格 + 标尺 + 编号(内置 3×5 点阵字)+ 每格指标(coverage/mean/bbox/md5) 4 张 → 1630×434(≤1700 不触发二次缩放);2×2+grid+标尺 → 858×880
C5 框选 + 宫格 aabb=[…] / focus="x,y,z,r"(顶层或按视角);sheet=N 出 N 宫格 —
C6 GIF qc_load 在 Blender 给 0×0 时走内置纯 Python GIF 首帧解码(GCT/LCT、隔行、透明索引)+ gif_to_png 4 张真图版(1200×1371 / 2500×1440 / 2500×1576 / 2500×1615)逐像素正确
C7 分片 / 长活 chunk=[i,n];as_job/auto_job_ms;>60 s 提示 + A2 台账 chunk=[2,2] → 只渲第 2、4 张
C8 可发现性 worker / loop 的量化判定句写进工具描述 同脚本 ≥3 次或单次 >10s 迭代 → worker;搜 ≥20 次且每次出图 → loop

C6 的一处更正:Blender 5.2.2 本身已能读 GIF。v0.8.9 那次"GIF → 0×0"的验证用了我自制的坏 GIF,
结论有误 —— 本版保留兜底与明确报错,但"GIF 必 0×0"的说法作废。

D 组 · 环境与语义

  • D1 孤儿 datablock(我自己的漏):qc_render.py 只删 object 不删灯的 data → 每次调用漏 3 个 light;
    现在灯/相机 data 一起删,并在 finally 做计数自检。实测连跑 3 次 → delta [0,0,0]。
  • D2 headless(workdir=…)(脚本内 chdir + sys.path 首位)+ 路径归一化 + __file__ 语义写进文档。
  • D3 玻璃光学配方:blender_rt_preset(op="glass", args={targets:"MAT:x,OBJ:y", use_alpha:true}) ——
    一条调用设好材质侧折射标志 + 渲染方式 + Principled 输入名映射(4.x/5.x 的 "Transmission Weight" vs "Transmission")。
  • D4 作业台账:<工作目录>/jobs/index.jsonl + 每作业 summary.json;op=list 合并内存与磁盘 →
    后端重启后仍能列出历史作业。
  • D5 engine="none" 写进 schema(纯 python 任务免引擎前导,实测 1288ms;blender.exe 启动 ~1 s 省不掉)。
  • D6/D7/D8:一次调用=一个部位的纪律、audit_duplicates 报同名分叉资源、像素底行优先写进文档。

A0 · 版本自证(本轮事故的元教训)

他们读的是含 v0.8.9 修复的源码、跑的是 v0.8.8 的 lib,把已修的问题又排查了两轮。现在:

  • 所有工具描述带 [v0.8.10] 前缀(描述由已加载的 lib 生成 → 会话里一眼看出跑的是哪一代);
  • blender_viewport(op="status") 返回 pluginVersion + runtime 模块指纹 + 最近 run/job 台账 + 磁盘台账路径。

E1 · 制度

新增 docs/证据分级与读图纪律.md:L0–L3 分级证据(只有 L2/L3 才允许读图)、每轮读图预算(≤6 张 / ≤800×450)、
读图前写 2–3 条可测断言、读图后三分法("未经证实的视觉印象"不得写成"通过")、以及每条 op 产出哪一级的映射表。

安装 / 升级

解包附件 → bash scripts/build.sh → 按 README.md 第 2 节注入。已在 v0.8.9 及更早:直接升到 v0.8.10
(含 v0.8.9 的流式回执修复 —— 那条对"长活假失败"是决定性的)。

v0.8.8 — 内置多视角渲染 harness(P2-1)

Choose a tag to compare

@sixtysevenlf sixtysevenlf released this 15 Sep 15:40

v0.8.8 — 内置多视角渲染 harness(P2-1)

外部反馈(第 4 份)最后一项:入参 {file, views[], res, samples, thr, outdir} → 自动按 bbox 精确取景 + 固定三点光 + 逐张 jsonl 计时 + 不超过 budget 的判定。本轮收敛成插件内置能力,建模任务不用再各写一遍。

新增

  • runtime/qc_render.py(挂 K.dsh_qc_render_api),入口 blender_rt_plan(op="qc_render_views", args={...});
    args.asJob=true 自动转作业层(无头进程 = 场景天然隔离),无头里也可 preload="qc,qc_render" 直接用。
  • 自动取景:合并所有可见 mesh 的 AABB(targets 可限定)→ 逐角解算相机距离 + margin 留白(默认 1.12);
    具名视角 front/back/left/right/top/bottom/iso/iso_l/iso_back/front_high/right_high,也支持 az=35,el=20、显式 from/look_at、正交。
  • 固定三点光:SUN key 3.2 / fill 1.0 / rim 2.4,角度固定且相对相机方位角(+35°/45°、-50°/8°、+165°/30%);
    只在本进程内临时建,出图后删除并把 scene 渲染设置全部还原 —— GUI 实测对象数/相机数/灯数/视口帧 hash 全部不变。
  • 逐张渲染 + jsonl:<outdir>/render_views.jsonl 一行一张,含 {view, ms, bytes, hash, path, res, engine, samples, camera, frame{bbox, margin_px, coverage, pred_bbox_px, proj_err_px}}。
  • 预算判定:budget_s(别名 thr)累计毫秒超限立即停 → within_budget=false + stopped_early + skipped_views,已出的图与行保留。
  • 可选参考比对:ref_path / ref_box(可按视角给字典)逐张调 qc.py 的 compare(默认固定对齐口径),IoU/Dice/边界距离/剖面差写进同一行。

实测(preserve_scene_before_orca.blend / 350 个可见 mesh / 512×512 / 64 采样 / EEVEE+光追)

视角 渲染 ms bytes alpha bbox 四边余量 px
iso(-45°,25°) 2124(首张含着色器编译) 154604 [83,96,428,458] 83 / 96 / 83 / 53
front(-90°,0°) 798 162515 [36,36,475,475] 36 / 36 / 36 / 36
right(0°,0°) 565 192160 [36,36,475,475] 36 / 36 / 36 / 36
  • 3 张共 3487 ms(进程内 3571 ms)· jsonl 3 行 · within_budget=true(预算 120 s)。
  • 取景对拍:自算矩阵 vs Blender world_to_camera_view 的逐点像素差 0.000009–0.000081 像素;11 个具名视角 selftest 全部落在画幅内(fill 0.79–0.89 = 1/margin 量级)。
  • 预算对照跑(budget_s=1.0):只出 1 张即停,within_budget=false、skipped_views=["front","right"]、render_ms=2113,已出的 p21c_iso.png 保留。
  • GUI 入口:blender_rt_plan(op="qc_render_views") 在 95 对象场景 320×320/16 采样跑通(659 / 283 ms),现场检查:对象数、相机数、灯数与视口帧 hash 全部不变,无 DSH_QC* 残留。
  • 作业层:asJob=true → job-mu2tx2vu57i status=done / exit=0 / 28685 ms,产物 4 项(3 图 + jsonl)。
  • 手写流程对照(同一机位):alpha 通道逐像素完全一致(几何/机位等价);RGB 最大差 5/255、且只在 0.046% 像素上 —— 对照实验里同装置连渲两张自身就有 1/255 差异 → 差异属 EEVEE+光追随机采样噪声,判据看 alpha/轮廓,不要拿像素级 md5 当判据。

同版本一并(v0.8.6 / v0.8.7,之前未单独发版)

  • v0.8.6:修 blender_rt_perf 的 dsh_perf_status NameError(注册字典延迟求值)。
  • v0.8.7:长任务自动转作业(headless(as_job=true) 返回 jobId + 日志路径)、场景世代号 sceneEpoch、脚本命名空间 DSH_OUT/DSH_WIN/DSH_WSL、错误增强(库占用/无相机/UNC)、cookbook 的 measure 模板与两条 Blender 语义坑。

安装 / 升级

解包附件(tgz 或 zip)→ 进入插件目录 → bash scripts/build.sh(链接依赖并编译 lib/)→ 按 README.md 第 2 节注入 DSH。
Blender 侧装 addon 并 Connect(socket 127.0.0.1:9876)。配置走 runtime/config.mjs(env → 配置文件 → 自动探测),换机器不用改源码。

从 v0.8.2 及更早版本升级:直接升到 v0.8.8(已含 v0.8.3–v0.8.7 全部修复)。

v0.8.5 - job layer / hot reload / QC mask interval

Choose a tag to compare

@sixtysevenlf sixtysevenlf released this 15 Sep 06:07

v0.8.5 — 作业层 / 热重载 / QC 掩膜区间

  • blender_rt_job(第 15 个工具):start / status / collect / kill / list。实测:start 0.08 s 返回 job id(不阻塞);立刻 status=running;15 s 后 collect = done + result + 40 项产物 + 日志路径;kill = killed。日志与产物在 outdir/jobs//。
  • 模块热重载:K.reload_modules(prefixes, root) + rt_worker exec 的 purge_prefix。实测:改文件后不 purge 读到旧值 1,purge 后 2,purged 列表正确。
  • QC 掩膜区间:qc_mask_sweep → iou_min / median / max + spread + 推荐口径。实测真实参考/渲染对:0.5914 / 0.6319 / 0.9324,spread=0.341(比反馈估计的 ±0.2 更大)→ 报告必须给区间。
  • 同线(v0.8.4):worker 异常把 error / traceback 提级到信封层(实测抛错带类型与回溯,不再是 unknown);正斜杠 UNC 归一化为反斜杠 UNC(此前被当相对 .blend 解析);新增内核助手 K.stage 兜底拷贝(实测 UNC 加中文路径 → 本地路径 → 读到 640x640)。
  • 文档:UNC 写入实测可写(旧的绝对化结论已改);双斜杠是相对 .blend 的前缀;WSL 环境变量不跨进 Windows 的 blender.exe;cookbook 新增「哪些时候别用」表 + silhouette-fit 一键配方 + 无窗口要走 bmesh 的推荐姿势。

安装

解包附件后进入插件目录,跑 bash scripts/build.sh(链接依赖并编译 lib)。需要 Blender 里启用 MCP for Blender addon 并 Connect(socket 127.0.0.1:9876)。

升级建议:v0.7.0 与 v0.8.0 用户直接升到 v0.8.5(v0.8.1 修了 /act 路由回归,v0.8.4/0.8.5 补齐错误可见性与作业层)。