# RE backlog Open items that are *not* being worked right now. Each entry says what is wrong or unknown, what evidence exists, and what the first step would be. Move an item into `INDEX.md` (with a `structures/…md` or a parser + test) once it is actually settled. --- ## Capital ships assemble wrong in the viewer **Reported:** 2026-07-30, by the user. **Status:** πŸ”Ž **diagnosed 2026-08-10 β€” the format layer is exonerated.** Runtime captures of three classes (`f105`, `e105`, `e106`) at controlled range reproduce `assemble_ship` to ≀0.43 units in translation and to 0.000 in rotation for every part that does not move; see [`ship-placement-capture-generalisation.md`](ship-placement-capture-generalisation.md) Β§4. So look at **the viewer**: first that it passes `include_external = true` (`iso_loader.rs:4012` β€” with `false` an e106 loses its bridge and both nacelles, 5 parts instead of 11), then its own transform stack. One real format-side bug was found on the way and is **fixed**: index-less parts (`e105_brg`) never matched their `GN_Bridge_01` hardpoint, so 34 (stage, ship) entries β€” `e102`, `e104`, `e105` across Stages 02–29 β€” assembled without a bridge. The other apparent exception (`e105_eng_01` rotation) was an aggregation artefact and is 0.000. The original report and its reasoning follow. The reborn viewer builds capital ships from the split XBG7 parts via `sylpheed-formats::ship::assemble_ship`, and they come out **wrong** β€” parts in the wrong place / wrong orientation. **Why this is a real finding and not a known limitation:** the RE write-up [`ship-placement-runtime-capture.md`](ship-placement-runtime-capture.md) declares static assembly βœ… **exact** as of 2026-07-26 β€” 9-channel joint tables `[TX TY TZ RY RX RZ SX SY SZ]`, Euler `RyΒ·RxΒ·Rz`, with `ship::tests::static_assembly_matches_runtime_capture` asserting static == runtime capture (T < 1.0, R < 0.02). So either the viewer is not using that path, or the claim generalises worse than the test suggests. **The likely gap:** that test is **one ship** β€” the `e106` destroyer, 8 parts plus two nacelles, two turrets and the hull mirror. Nothing pins the other classes. Rules that were derived from `e106` and could easily be `e106`-specific: - the engine cluster rig mounted at `GN_Engine_01` (two mirrored nacelles + centre); - "X-reflect the shared-geometry twin whose lateral offset opposes the geometry's dominant side" β€” a heuristic, not a decoded flag; - cross-id turret instancing (Γ—2). **First step (the oracle already exists):** re-run the runtime capture on a *different* capital ship and diff static vs captured, exactly as `e106` was done β€” F10 in the `capture-ship-placement` build of `xenia-canary-native` dumps the ship shader's `c0..c2` WorldViewProjection rows per part; `WV_ref⁻¹ Β· WV_p` is the ship-space rigid transform, which is ground truth. Pick a class whose rig differs from `e106` (different engine count, a ship with no `sld`, a carrier). Then extend `static_assembly_matches_runtime_capture` into a per-ship table so a regression in one class cannot hide behind `e106` passing. **Also worth ruling out first, cheaply:** that the viewer's own transform stack (scale, handedness, node-instance recursion) is not re-breaking a correct assembly β€” compare the viewer's placement against `assemble_ship`'s output directly before blaming the format layer. --- ## Viewer: `include_external` is already on β€” that hypothesis is dead **Checked 2026-08-11.** The item above names "first that it passes `include_external = true` (`iso_loader.rs:4012`)" as the cheap first step. It does: `ShipBrowser::show_external` defaults to `true` (`iso_loader.rs:643`), the checkbox reads it (`ui.rs:1593`) and it is threaded through `RequestShipRender` β†’ `build_ship_model` β†’ `assemble_ship` unchanged (`ui.rs:1689`, `iso_loader.rs:4012`). So a ship rendered by the viewer is the full external assembly, not the bare hull. The viewer also does not have a transform stack of its own to blame: it bakes `ScenePart::apply` straight into the vertices and rotates normals by the same `p.m` (`iso_loader.rs:4030-4062`), so its placement is `assemble_ship`'s output by construction. What remains unexcluded, in order of cheapness: the mirror handling (`det < 0` reverses triangle winding only β€” a reflected part keeps its reflected geometry), `Xbg7Model::models_named` resolving the wrong sub-model when a resource name repeats, and the exhaust cones. **Next step is a visual**: the diagnosis has run out of things it can settle by reading, so the viewer needs to be run against a known-good class (`e106`) and its render compared with `ship_render`'s. --- ## Viewer: the duplicate-resource-name hypothesis is dead too **Checked 2026-08-11.** The diagnosis above left three candidates for why capital ships assemble wrong in the viewer: mirror handling, `Xbg7Model::models_named` resolving the wrong sub-model when a resource name repeats, and the exhaust cones. The second is now **refuted**, and comprehensively. `build_ship_model` resolves each placement with `base.iter().find(|m| m.name == p.resource)` (`iso_loader.rs:4041`) β€” first match wins β€” so a repeated resource name inside a container would silently draw the wrong geometry. It cannot happen: decoding **every** XBG7 resource in **all 22 stage containers** gives **4 603 resources and zero repeated names**. ``` Stage_S01 62/62 Stage_S07 323/323 Stage_S13 290/290 Stage_S25 351/351 Stage_S02 304/304 Stage_S08 388/388 Stage_S14 22/22 Stage_S26 318/318 Stage_S03 214/214 Stage_S09 316/316 Stage_S15 386/386 Stage_S27 321/321 Stage_S04 179/179 Stage_S10 7/7 Stage_S16 65/65 Stage_S28 118/118 Stage_S05 92/92 Stage_S11 157/157 Stage_S24 162/162 Stage_S29 386/386 Stage_S06 266/266 Stage_S12 376/376 ``` Per-ship it is tighter still: `e106` wants 9 distinct names and decodes exactly 9 models for 11 placements; `e105` 9 for 9; `f105` 5 for 6. Every placement resolves to the one model it names. **So two of the three candidates are gone** (this one and `include_external`), leaving **mirror handling** and **the exhaust cones** β€” and the still-untried visual comparison, which remains the right next step. --- ## Viewer: mirror handling and the exhaust cones are cleared too β€” the static avenue is exhausted **Checked 2026-08-11.** Both remaining candidates were tested across every ship on the disc, and neither shows the reported signature. **Mirror handling.** The concern was that `ScenePart::apply` bakes `RΒ·(SΒ·v)+T` while the viewer takes its winding-flip decision from `det(m)` alone and rotates normals by `m` alone β€” both ignoring `s`. A mirror encoded as a *negative scale* would then reflect geometry without flipping winding, drawing the part inside-out. It never happens: across **1 485 assembled parts** in all 22 containers there are **22 mirrored parts, every one with `det(m) < 0`**, and **zero** parts with a negative scale or a non-uniform one. `apply_twin_mirrors` writes the reflection into `m` (negating its X column), so the viewer's flip always fires, and ignoring `s` for normals is harmless because `s` is always uniform. **Exhaust cones.** These are the one piece of geometry the viewer *invents* β€” a cone at each `GN_Jet`/`GN_SJet` frame, because the real engine geometry is recessed and the game draws FX there instead. If they landed wrongly they would read exactly as "a part in the wrong place". Across **335 assembled ships, 192 of which have exhaust frames, not one cone sits outside its hull's bounding box** (tolerance 10 % of the axis span). **Caveat, stated rather than glossed:** "inside the hull box" does not prove a cone is *right* β€” orientation and size are untested, and a cone could be wrong while still inside. What it does rule out is the reported symptom for that part. So every mechanism this diagnosis proposed is now eliminated: `include_external`, duplicate resource names, mirror handling, and cones-in-the-wrong-place. The format and assembly layers pass every static test available, and **the visual comparison is no longer merely the next step β€” it is the only remaining one.** Render `e106` in the viewer beside `ship_render`'s output of the same `assemble_ship` result; if they agree, the bug is in neither and the original report needs re-grounding against a specific ship and a specific expectation.