diff --git a/docs/re/INDEX.md b/docs/re/INDEX.md
index 13bf143..b64350c 100644
--- a/docs/re/INDEX.md
+++ b/docs/re/INDEX.md
@@ -20,7 +20,7 @@ Promote to a prose `structures/β¦md` file when a format needs behavioural notes
| 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) | hull placement static-exact; external parts approximate statically. **Runtime capture** (Canary F10 β VS-constant WorldView) gives ground truth β validated on `e106` destroyer; not yet baked into the viewer |
+| 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** β 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 |
| 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 |
diff --git a/docs/re/SESSION-2026-08-11.md b/docs/re/SESSION-2026-08-11.md
new file mode 100644
index 0000000..5cbc9fc
--- /dev/null
+++ b/docs/re/SESSION-2026-08-11.md
@@ -0,0 +1,69 @@
+# Session log β 2026-08-11 (autonomous run)
+
+What changed today, in the order a reviewer would want it, with the one item
+that needs **your decision** called out first.
+
+## Needs a decision from you
+
+**Editing save slot 01.** Save files can now be written back
+([savegame_edit.py](../../tools/re-capture/savegame_edit.py) re-wraps a real save
+**byte-identically**), and hand-written saves load. That unblocked several
+questions by probing throwaway slots. But the **title-menu screens read the
+auto-save (slot 01)**, so the remaining save-field questions (the difficulty
+enum, `SHAB.b`) need slot 01 itself edited β the only save with real progress.
+Not done. Recoverable (full content backup + slot 01's bytes archived), but it is
+your call. Everything else below was done in throwaway slots.
+
+## Findings, by area
+
+### Save format
+- **The develop differential**: buying one Arsenal weapon moves exactly three
+ fields β Points `+24` (which **separates it from `+28`**), clear ratio `+8`
+ (so the ratio counts *collection*, not stages), and two entries of the 54-byte
+ blob. Blob alphabet: `0` locked / `2` developable / `4` owned; only the `4`s
+ are stored, `2` is re-derived at load.
+- **The blob's index space is solved** β it follows `strings.tbl`'s item order,
+ **cut items included**, pinned by four probe saves and closing exactly at 53.
+- **`SHAB` is the per-stage record table**: record 0 = Stage 01, and `c` is its
+ best clear time in ms (`324773` = the `05:24.77` MISSION SELECT shows).
+- **Refuted**: every GHAD scalar as stage/difficulty (16 elements probed), and
+ "stage = filled `SHAB` count + 1".
+
+### Formats
+- **T8aD 96 % β 100 %** of the disc: a surface is a list of **sub-rectangles**
+ (`dst X, dst Y, w, h` per rectangle), not a 256 grid. The "~15 % variants" were
+ a wrong model.
+- **LSTA**: the count was never unreliable β it counts **sprites *and* `PRMD`
+ primitives** (64/64 exact).
+- **RATC**: "one level deep" is not a limitation; nested records are **leaves
+ that reference siblings by name** (10 144/10 148 resolve).
+- **UI screens are fully reconstructible from their bundle** β element list +
+ order (declaration table, with a **parent index**) and placement/animation
+ (keyframe region). **Validated against the running game to Β±2 px.**
+- **Movie β subtitle β voice link solved statically** (101 movies) β `INDEX` had
+ it filed as needing the running game.
+- **XPR2 channel order confirmed** against the game (hue 23.3Β° vs 9.3Β°).
+
+### Meshes β the user-reported viewer bug
+- Root-caused, **after one wrong turn I corrected in place**: the giant slab on
+ `e106` is not a stray volume but an **XBG7 mis-decode** β the same resource is a
+ 49Γ23Γ42 turret in 11 containers and 1600Γ2100Γ4800 in 3.
+- **New detector**: a resource shared across containers must decode to the same
+ bounds. **125 of 681** shared resources fail that check β a measured lower
+ bound on *silent* mis-decodes (distinct from the 814 honest declines).
+
+## Two disc defects found
+`SUBTITLE_S12B.tbl` (referenced by the movie table, present in **no** language)
+and `pmbase.t32` (placed by `pmbase.rat`, present **nowhere**). A reimplementation
+should skip a missing asset rather than treat it as a decode failure.
+
+## Process notes worth keeping
+- Several "the data is inconsistent" comments meant **our model was incomplete**
+ (T8aD variants, the LSTA count). Re-test such notes rather than routing around
+ them.
+- A test that walks the *oracle's* items and looks each up in our output **can
+ only fail one way** β it never sees extras. That shape hid the ship bug and,
+ earlier, `include_external`.
+- When a diagnosis has eliminated every hypothesis and the last step is *a
+ visual*, do the visual first. Three iterations went by before a 30-second
+ render showed it.