docs(p2-72a): 📋 scope array-removal increment + correct the save-format coupling claim

Read-only analysis of the npc_buildings serialize chain (no code touched;
game_state.gd unchanged). Finding: the resume map's "array removal forces a
SaveManager rewrite, cannot land independent of save-format-migration" claim is
over-stated. `_serialize_npc_buildings` → `serialize_npc_buildings` calls
`b.to_dict()` on each `Building` view, and that view's `to_dict()` already
proxies to `_gd_state.npc_building_dict(_idx)` — serialized bytes are already
sourced from the Rust mirror. Re-pointing serialize at the mirror directly is
byte-identical; the save FORMAT does not change. This objective owns the whole
increment.

STOPPED this session deliberately (gate-feasibility, not coupling): the increment
edits the highest-regression live-world-map file and must land as one green commit
gated by GUT-headless + cargo + build:gdext + operator-gated world-map visual
proof. Godot import cache is cold (host-freeze risk per godot-docker.sh), no
mc-godot Docker image / warm volume exists, and the visual proof needs operator
sign-off — not completable safely without a runway session. Precise execution
recipe (serialize re-point + 6 reader retargets + array/spatial-index/view-fn
deletion + gate steps) recorded for the next session. Status stays partial.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
autocommit 2026-06-04 20:06:53 -07:00
parent bbacf5ff8d
commit 5c578e0d01
2 changed files with 73 additions and 1 deletions

View file

@ -1,5 +1,5 @@
{
"generated_at": "2026-06-05T02:59:51Z",
"generated_at": "2026-06-05T03:06:38Z",
"totals": {
"done": 243,
"in_progress": 1,

View file

@ -389,3 +389,75 @@ migration, not a demo feature.
**Effort:** M — the array-removal increment is ~1 session but requires the full
build+GUT+visual cycle and is gated behind the save-format GDScript side.
## 2026-06-04 Wave-D session 2 — array-removal increment SCOPED, not executed (clean stop)
**Not advanced — deliberately stopped after the disqualifier checks. Rust surface
unchanged; `game_state.gd` UNTOUCHED. Status stays `partial`.**
### The load-bearing NEW finding — the "forces SaveManager rewrite" blocker is OVER-STATED
The 2026-06-03 resume map (step 3) claims array removal "forces the SaveManager
rewrite" and so "cannot fully land independently of Stage 3's GDScript side."
**Verified false by reading the serialize chain (read-only, no edit):**
- `game_state.gd:699 _serialize_npc_buildings()``SerializationHelpers.serialize_npc_buildings(npc_buildings)`.
- `game_state_serialization_helpers.gd:9-14`: that helper iterates the array and calls
`b.to_dict()` on each element.
- Each element is a `BuildingScript.new(self, i)` view (`game_state.gd:541`), and
`building.gd`'s `to_dict()` proxies to `_gd_state.npc_building_dict(_idx)` — i.e. the
serialized bytes are **already sourced from the Rust mirror**, not from any
GDScript-owned field.
**Therefore re-pointing serialize at the Rust mirror directly (iterate
`range(_gd_state.npc_building_count())``npc_building_dict(i)`, or
`npc_buildings_all()`) is byte-identical** — the save FORMAT does not change, only the
(now-redundant) GDScript array indirection is dropped. This is NOT save-format-migration
scope; **this objective owns the whole increment.** Brief and objective reconcile.
### Why it was still STOPPED this session (gate-feasibility, not coupling)
The increment edits the highest-regression live-world-map file and per never-regress
must land as ONE green commit gated by GUT-headless + cargo + build:gdext + an
operator-gated world-map visual proof. Three blockers to gating *this session*:
1. **Godot import cache is COLD** (`src/game/.godot/` absent). `scripts/godot-docker.sh`
documents that a cold `godot --headless --import` "kernel-panicked plum twice and
freezes apricot when run cold" — so a direct flatpak GUT run on this RUN host carries
a host-freeze risk. The safe path is the Docker runner, but **no `mc-godot` image is
built and no warm-import volume exists** — first-run is a heavy cold build.
2. **Committed `.so` (2026-06-03 22:16) predates a clean `./run build:gdext`** — a proper
gate wants a fresh rebuild first (the Phase-4 refactor was import-path-only so the
`GdGameState` accessor surface is behaviourally identical, but gating should not assume).
3. **The world-map visual proof is operator-gated** — cannot be self-approved (phase-gate
protocol + `feedback_phase_gate_screenshots`).
A half-routed tree (readers moved, serialize re-pointed, but array delete ungated) is
worse than not starting. Clean stop is correct.
### Precise execution recipe for the next session (with runway + Docker warm)
All as ONE commit, then gate:
1. **Re-point serialize:** `_serialize_npc_buildings()`
`SerializationHelpers.serialize_npc_buildings` reads from
`range(_gd_state.npc_building_count())` / `npc_building_dict(i)` instead of the array.
(Byte-identity proven above — confirm with a save→reload→save byte-diff in GUT.)
2. **Route the 6 direct readers** off `GameState.npc_buildings` /
`get_npc_buildings_at()` onto `_gd_state` accessors:
`modules/ai/wild_creature_ai.gd:56,148`; `modules/management/rust_fauna_integration.gd:76`;
`rendering/lair_overlay_renderer.gd:43`; `map/entity_finder.gd:161`;
`generation/village_lair_placer.gd:53` (already uses `get_npc_buildings_at`
swap to `_gd_state.npc_building_at`); `entities/building.gd` is itself the view (keep
thin or delete + inline `npc_building_dict`). Mind reader dict-shape compatibility
(tuple extraction vs Vector2i) — each reader uses the data differently.
3. **Delete** `game_state.gd:113 npc_buildings`, `:115 _npc_buildings_by_tile`,
`:530 _rebuild_npc_buildings_view()`, `get_npc_buildings_at`/`get_all_npc_buildings_of_type`
(or re-point them at the Rust spatial index `npc_building_at`), and the reset lines
`:263-264`. Remove `_rebuild_npc_buildings_view()` calls at `:492,:526,:729`.
4. **Gate:** `./run build:gdext` → warm import via `scripts/godot-docker.sh --headless
--import` (Docker, freeze-safe) → GUT headless (the entities + serialization suites)
`./run screenshot` world-map proof → read it back + present for operator sign-off.
Do NOT flip the visual-dependent bullets ✓ unilaterally. K rises ~5/11 → ~8/11;
objective stays `partial` until the save-format-migration + render-source bullets close.
**Blocker for next session:** none technical — needs a session with runway to build the
Docker godot image + warm the import cache, and an operator present for the visual-proof
sign-off.