From eed732446c0a18be4c5917d6869cd112e7b0c21c Mon Sep 17 00:00:00 2001 From: m3ultra Date: Sun, 19 Jul 2026 11:55:38 +1000 Subject: [PATCH] =?UTF-8?q?docs:=20FABLE=5FSTARTUP=5FPACKET=20=C2=A78=20?= =?UTF-8?q?=E2=80=94=20adopt=20path=20needs=20NO=20weight=20conversion=20(?= =?UTF-8?q?Jourloy=20loads=20originals=20directly;=20convert=5Fweights.py?= =?UTF-8?q?=20retired=20as=20double-transpose=20trap)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 --- docs/FABLE_STARTUP_PACKET.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/FABLE_STARTUP_PACKET.md b/docs/FABLE_STARTUP_PACKET.md index 4b0f18a..52ef298 100644 --- a/docs/FABLE_STARTUP_PACKET.md +++ b/docs/FABLE_STARTUP_PACKET.md @@ -361,3 +361,14 @@ fixture table where it drifted): CPU/inspection while the box is busy. - **Unchanged:** §6 step 5 (single A0 GPU gate — seed 42, 1024-cascade, compare 124.8 s) is still the **only** job that must wait for a free box; §6 step 6 (Hunyuan Target 1) still safe in parallel. + +## 8 · CORRECTION — the adopt path needs NO weight conversion (2026-07-19, verified in source) + +Round-2 flagged three bugs in `convert_weights.py` (bf16 crash, decoder downcast, ss_dec double-transpose). **All three are moot: the converter is not needed for the adopted Jourloy base, and using it would corrupt.** Verified by reading Jourloy `mlx_backend/`: + +- **Weights load directly from the original TRELLIS.2-4B safetensors.** `pipeline.py:116/141/162` → `load_safetensors(".safetensors")` → `model.load_weights(...)`. `load_safetensors` (`__init__.py:26`) is just `mx.load(path)`, keeping native bf16/fp16; an optional `dtype=` casts float weights (this is the M1 fp16 path — **not** a separate converter). +- **Remap = key-rename ONLY, zero tensor transform.** `remap_flow_model_weights` / `remap_vae_decoder_weights` only rewrite key strings (nn.Sequential `.N.`→`.layers.N.`; MlxSparseLinear `.linear.` wrap). No transpose, no dtype change to any tensor. +- **All conv transposing is IN-MODULE at forward.** `structure_decoder.py:14` transposes the dense ss_dec (Co,Ci,K,K,K)→(Co,K,K,K,Ci) every forward; `sparse_conv.py:130` `w.transpose(1,2,0)` for the sparse GEMM. So the modules EXPECT PyTorch-format weights. +- **Why the converter is a trap:** it transposes ss_dec at load; the module transposes again at forward → double-transpose. For conv1/conv2 (Ci==Co==channels) the SHAPE still matches, so `load_weights` succeeds silently with wrong values; for input_layer (Ci=latent≠Co) it would shape-mismatch and error. Mixed silent/loud corruption. + +**ACTION for Fable:** do **not** run `convert_weights.py` for the Jourloy path (it's now banner-warned in staging). For M1 fp16, pass `dtype=mx.float16` to `load_safetensors`. §5's transpose table remains correct as a *description of the on-disk layout*, but the CONVERSION step it implied is unnecessary — Jourloy consumes originals directly. A converter is only relevant if you ever build a from-scratch decoder that does not transpose at forward.