diff --git a/docs/re/BACKLOG.md b/docs/re/BACKLOG.md
index 3d2b6fe..19585aa 100644
--- a/docs/re/BACKLOG.md
+++ b/docs/re/BACKLOG.md
@@ -8,8 +8,30 @@ unknown, what evidence exists, and what the first step would be. Move an item in
## 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`,
+**Reported:** 2026-07-30, by the user. **Status:** β
**format-side cause found and
+fixed 2026-08-12** β see below for the 2026-08-10 diagnosis this supersedes.
+
+The remaining format-side defect this entry pointed at (a shared turret decoding
+~100Γ too large in some containers) was real and is gone. `e303_wep_01` decoded
+as a 1600Γ2100Γ4800 block in `Stage_S02`, swallowing the `e106` hull; requiring an
+index buffer to cover its vertex pool **exactly** moved it to the block every
+other container agrees on, and it now decodes 49Γ23Γ42 everywhere and places at
+Β±179 on the hull. The same fix repaired `e106_bdy_03` (a 600Γ1600Γ998 slab) and
+moved 29 anchors disc-wide, 22 of which had been carrying **another resource's
+geometry under their own name**. See
+[`structures/xbg7-mesh.md`](structures/xbg7-mesh.md).
+
+Two things are worth carrying forward rather than closing:
+- the assembler was **audited and exonerated** β every composite node carries
+ scale 1.0 and an orthonormal matrix, so nothing on that side inflates a part;
+- **no metric caught this.** Coverage, cross-container consistency, the capture
+ oracle and the twin invariant were all green while a 1 600-unit slab sat through
+ the ship. It was found by *rendering the ship and looking at it*, and the
+ numeric screens written afterwards to automate that check both failed.
+
+The 2026-08-10 diagnosis follows, and its viewer-side pointers still stand.
+
+**Status (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)
diff --git a/docs/re/INDEX.md b/docs/re/INDEX.md
index 4c101eb..301fe00 100644
--- a/docs/re/INDEX.md
+++ b/docs/re/INDEX.md
@@ -19,8 +19,8 @@ Promote to a prose `structures/β¦md` file when a format needs behavioural notes
| LSTA sprite list | β
| `sylpheed-formats/src/lsta.rs` | A display list of inline elements: **T8aD sprites and `PRMD` primitives**. The `count` at `0x04` is **exact and counts both** β `count == T8aD + PRMD` for **64/64** lists on the disc, which retires the old "a few entries disagree" note (it compared sprites against a total including primitives). **All 1 281 sprite frames decode** after the T8aD rectangle-list fix |
| IXUD subtitle | π‘/β
| `sylpheed-formats/src/ixud.rs` + [movie link](movie-subtitle-link.md) | timed cues. **The movieβsubtitleβvoice link is solved β statically**, from the movie config record in `tables.pak` (schema `0x067025b9`), not from the running game as this row previously assumed: [101 movies mapped](captures/movie-subtitle-voice-map.csv), 94 with subtitles, 83 with voice, 21 with a telop overlay. 93 of 94 subtitle refs resolve in the language paks; **`SUBTITLE_S12B.tbl` is missing from all six languages** β a dangling reference on the disc. Naming is `SUBTITLE_.tbl` / `VOICE_` with six documented exceptions. The record's ~104 **script ids** are β β positional pairing drifts by three because the IDXD pool dedupes repeated values |
| Fonts (ttf/otf/ttc) | β
| `sylpheed-formats/src/font.rs` | standard OpenType, parsed via ttf-parser |
-| XBG7 mesh | π‘/β | `sylpheed-formats/src/mesh.rs` + `tests/mesh_disc.rs` ([xbg7](structures/xbg7-mesh.md)) | weapons/props: declaration-driven variable stride (36 models), GPU-confirmed. **Stage containers: 5662 sub-models across 22 stages** via content-anchored grouped pools (`stage_models`). **Declined set now measured**: 6 294 resources, **5 480 decode (87.1 %), 814 declined** in 31/166 containers β and it is *not* "a few quantized hero bodies" but 492 `e*`, 142 `f*`, 73 `n*`, 23 `eff*` plus `*_dead` variants. The descriptor does **not** declare decodability (word[2] is a sub-mesh count; decoded and declined appear at every value), so the gap is the anchor scan, not an unread format flag β see [xbg7](structures/xbg7-mesh.md). **Silent mis-decodes also exist and are now measurable**: a resource shared across containers must decode to the same bounds, and **125 of 681 shared resources fail that check** with identical vertex/triangle counts β the decoder picked a different buffer of the same size. A majority vote across containers would resolve 104 of them (14 are 50/50), but that is π‘ unvalidated beyond the one case with a render and a capture behind it |
-| Capital-ship part placement | β
/π‘ | `sylpheed-formats/src/ship.rs` (static) + [runtime capture](ship-placement-runtime-capture.md) | Placement itself is **sound**: hull static-exact, validated against the `e106` runtime capture, and cross-id mounting is genuinely narrow (**2 pairs across 335 ships**). **The user-reported "ships assemble wrong" is NOT a placement bug** (a locality fix for it was written and withdrawn β it regressed the twin mirror) β it is an XBG7 **mis-decode** that makes one shared turret 100Γ too large in 3 of 14 containers ([backlog](BACKLOG.md), [xbg7](structures/xbg7-mesh.md)). Also open: `static_assembly_matches_runtime_capture` walks capture parts only, so **extra** static placements can never fail it |
+| XBG7 mesh | β
/π‘ | `sylpheed-formats/src/mesh.rs` + `tests/mesh_disc.rs` ([xbg7](structures/xbg7-mesh.md)) | **6 294 resources, 6 209 decode (98.7 %), 82 searched-and-missed** (2026-08-12, up from 5 480 / 87.1 %). Five evidence-driven fixes got there: **distinct anchor assignment** (no two resources may claim one buffer β proved by a capture showing the container holds both mirrored `e106` hull halves), the connectivity cap replaced by a **winding-consistency gate at 0.70**, **structural requirements on pre-pivot sub-meshes** (index range, then exact pool coverage), and **filtering after the assignment** so a subset query cannot differ from the full decode. Validated against a runtime capture that names the file offset of every buffer the engine drew: **46/46 drawn buffers claimed, 45 anchored exactly**. **No real mesh now decodes differently in different containers** β all 89 remaining cross-container disagreements are interchangeable 24-vertex bounding boxes, which no anchoring rule can pin (monotone order re-tested and refuted). Remaining misses attribute to the degeneracy/extent gate (42), winding (31) and coverage (9); the first was probed and its "obvious" fix refuted. Every decoded sub-mesh covers its own vertex pool |
+| Capital-ship part placement | β
| `sylpheed-formats/src/ship.rs` (static) + [runtime capture](ship-placement-runtime-capture.md) | Placement is **sound** (hull static-exact against the `e106` capture; cross-id mounting genuinely narrow, 2 pairs across 335 ships). The XBG7 mis-decode this row used to blame for "ships assemble wrong" β a shared turret ~100Γ too large in some containers β is **fixed** (2026-08-12, the exact-coverage requirement): `e303_wep_01` now decodes 49Γ23Γ42 everywhere and places at Β±179 on the `e106` hull, and no real mesh disagrees across containers. A composite-node audit confirmed the assembler itself never applied a bad scale (all nodes scale 1.0, orthonormal). Still open: `static_assembly_matches_runtime_capture` walks capture parts only, so **extra** static placements cannot fail it |
| Weapon fields defaulted on disc | β
| [runtime struct](structures/weapon-struct-runtime.md) Β· [DATA SHEET route](weapon-datasheet-runtime.md) | **Solved.** Canary maps guest RAM into `/dev/shm`, so the parsed `Weapon`/`Shell` objects are readable live; their layout is solved against disc ground truth (zero contradictions over 100+ records). All 126 weapons, exact numbers, no story progress needed β [4 393 values](captures/weapon-runtime-fields.csv) the disc does not carry. Supersedes the letter-bucket limit of the DATA SHEET route, which now serves as the independent cross-check |
| Unit (craft/vessel) fields defaulted on disc | β
/π‘ | [runtime struct](structures/unit-struct-runtime.md) | The parsed `unit\UN_*.tbl` definition object, vtable `0x820af844`, β₯`0x380` bytes, one per unit β **discovered, not assumed** (`unit_discover.py`), and distinguished from the spawned-entity class `0x820af030` by being one-per-ID and byte-constant within a run. Across runs only pointer words move β `--crosscheck` proves **no reported field offset is run-dependent** (two words, `+0x2c8`/`+0x2d0`, are stage-dependent and remain unidentified). 27 fields β
(21 units, 7 runs); the `Maneuver` block is **schema declaration order, 4 bytes/field, base `0x9c` with a two-slot gap after `AA_Roll_Min`** (29 anchors, 0 conflicts), which also pins 5 fields *no* disc record ever values. Angles are **radians at runtime, degrees on disc**. Unlike weapons, unit definitions are instantiated **per stage**, so coverage (21/110) grows by visiting missions β but a defaulted field is **not** a global constant: `Size_Y` provably inherits `Size_X` (7 independent units, 6 distinct values), and three more sibling rules are recorded β, recovering 65 values in units never visited β [values](captures/unit-runtime-fields.csv) |
| Arsenal develop economy | β
/β | [arsenal-develop-economy](arsenal-develop-economy.md) + [conditions](captures/arsenal-develop-conditions.csv) | The Arsenal reads `weapon.tbl` (item ids, in the 8-category display order) and `strings.tbl` (names, descriptions, and a **"Conditions to obtain"** block per item) out of `GP_HANGAR_ARSENAL.pak`. All **60** conditions are extracted: gates are stage completion, a predecessor item, or an **ace kill**; costs run 3 000β350 000 P and **20 items are free** once gated. `weapon.tbl`'s first record reproduces the in-game DATA SHEET exactly (Range D / Power E / Speed β / Weight 0.3 = Light / 4000 P) β later records are unreadable from the string pool alone because IDXD **dedupes repeated values**. Used to identify the save blob's index space, now **solved**: the blob follows **`strings.tbl`'s** order β the display order *plus* the cut items only the localisation file lists (`Adhesive Mine B2A`, `Ballista GSH`, β¦) β pinned by four hand-written probe saves (9 Stiletto, 21 Falcon, 39 Tomahawk, 48 Jamming System) and closing exactly at index 53. `weapon.tbl`'s id list is **not** the index space; that it is also 54 long is a coincidence, and the two agree only to index 32. The retail save's five unexplained owned entries are the cut items, shipped owned and never rendered |