## PR — Fix C++ build: move 2D slot creation out of header, guard/remove stale demo calls, and clean warnings

### 1) `src/cpp/SlotEngine.h` — **declaration‑only** for the Neuron‑touching API

```diff
--- a/src/cpp/SlotEngine.h
+++ b/src/cpp/SlotEngine.h
@@
 #pragma once
-#include <cmath>
-#include <utility>
 #include "Weight.h"
 
 namespace grownet {
 
 class Neuron; // forward decl
@@
-    // ---- Spatial stubs (Phase B) ----
-    inline std::pair<int,int> slotId2D(int anchorRow, int anchorCol, int row, int col) const {
-        // Placeholder: percent deltas per-axis; mirrors scalar approach.
-        const double eps = std::max(1e-12, cfg.epsilonScale);
-        const double denomR = std::max(std::abs(static_cast<double>(anchorRow)), eps);
-        const double denomC = std::max(std::abs(static_cast<double>(anchorCol)), eps);
-        const double dpr = std::abs(static_cast<double>(row - anchorRow)) / denomR * 100.0;
-        const double dpc = std::abs(static_cast<double>(col - anchorCol)) / denomC * 100.0;
-        const double bwR = std::max(0.1, cfg.binWidthPct);
-        const double bwC = std::max(0.1, cfg.binWidthPct);
-        return { static_cast<int>(std::floor(dpr / bwR)), static_cast<int>(std::floor(dpc / bwC)) };
-    }
-
-    inline Weight& selectOrCreateSlot2D(Neuron& neuron, int row, int col) const {
-        // Minimal: reuse scalar engine with FIRST anchor semantics on (row,col)
-        if (neuron.anchorRow < 0 || neuron.anchorCol < 0) {
-            neuron.anchorRow = row; neuron.anchorCol = col;
-        }
-        auto rc = slotId2D(neuron.anchorRow, neuron.anchorCol, row, col);
-        int limit = cfg.slotLimit > 0 ? cfg.slotLimit : 0x7fffffff;
-        int rb = std::min(rc.first,  limit - 1);
-        int cb = std::min(rc.second, limit - 1);
-        int key = rb * 100000 + cb; // simple packing
-        auto& slots = neuron.getSlots();
-        auto it = slots.find(key);
-        if (it == slots.end()) it = slots.emplace(key, Weight{}).first;
-        return it->second;
-    }
+    // ---- Spatial helpers (Phase B) ----
+    // Safe to keep inline (no Neuron access)
+    inline std::pair<int,int> slotId2D(int anchorRow, int anchorCol, int row, int col) const;
+
+    // Must touch Neuron → **declare only** here; define in SlotEngine.cpp
+    Weight& selectOrCreateSlot2D(Neuron& neuron, int row, int col) const;
 };
 
 } // namespace grownet
```

*(We keep `slotId2D` declared here; we’ll define both functions in the `.cpp` so the header never sees Neuron internals.)*

------

### 2) `src/cpp/SlotEngine.cpp` — **implement** the Neuron‑touching logic

```diff
--- a/src/cpp/SlotEngine.cpp
+++ b/src/cpp/SlotEngine.cpp
@@
-#include "SlotEngine.h"
+#include "SlotEngine.h"
+#include "Neuron.h"      // needs full Neuron for anchorRow/anchorCol/getSlots
+#include <algorithm>
+#include <cmath>
+#include <limits>
+#include <utility>
 
 namespace grownet {
 
+// Keep the math here so the header stays declaration-only.
+std::pair<int,int> SlotEngine::slotId2D(int anchorRow, int anchorCol, int row, int col) const {
+    const double eps    = std::max(1e-12, cfg.epsilonScale);
+    const double denomR = std::max(std::abs(static_cast<double>(anchorRow)), eps);
+    const double denomC = std::max(std::abs(static_cast<double>(anchorCol)), eps);
+    const double dpr    = std::abs(static_cast<double>(row - anchorRow)) / denomR * 100.0;
+    const double dpc    = std::abs(static_cast<double>(col - anchorCol)) / denomC * 100.0;
+    const double bwR    = std::max(0.1, cfg.binWidthPct);
+    const double bwC    = std::max(0.1, cfg.binWidthPct);
+    return { static_cast<int>(std::floor(dpr / bwR)),
+             static_cast<int>(std::floor(dpc / bwC)) };
+}
+
+Weight& SlotEngine::selectOrCreateSlot2D(Neuron& neuron, int row, int col) const {
+    // FIRST-anchor semantics on (row,col)
+    if (neuron.anchorRow < 0 || neuron.anchorCol < 0) {
+        neuron.anchorRow = row;
+        neuron.anchorCol = col;
+    }
+    const auto rc    = slotId2D(neuron.anchorRow, neuron.anchorCol, row, col);
+    const int  limit = (cfg.slotLimit > 0) ? cfg.slotLimit : std::numeric_limits<int>::max();
+    const int  rb    = std::min(rc.first,  limit - 1);
+    const int  cb    = std::min(rc.second, limit - 1);
+    const int  key   = rb * 100000 + cb; // simple packing
+
+    auto& slots = neuron.getSlots();
+    auto  it    = slots.find(key);
+    if (it == slots.end()) it = slots.emplace(key, Weight{}).first;
+    return it->second;
+}
+
 } // namespace grownet
```

> If your project already had a `SlotEngine.cpp`, add the two function bodies there.
>  If not, create it with the snippet above and make sure it’s compiled by CMake.

------

### 3) `src/cpp/RegionDemo.cpp` — remove/guard the **stale** `prune(...)` call

```diff
--- a/src/cpp/RegionDemo.cpp
+++ b/src/cpp/RegionDemo.cpp
@@
-    auto pruneSummary = region.prune(10000, 0.05);
+    // NOTE: C++ Region does not expose prune(...) yet; demo builds without it.
+    // auto pruneSummary = region.prune(10000, 0.05);
```

### 4) `src/cpp/demo/region_demo.cpp` — same guard

```diff
--- a/src/cpp/demo/region_demo.cpp
+++ b/src/cpp/demo/region_demo.cpp
@@
-    PruneSummary summary = region.prune(10'000, 0.05, 10'000, 0.05);
+    // PruneSummary summary = region.prune(10'000, 0.05, 10'000, 0.05);
```

*(If you prefer, we can add a tiny `Region::prune` stub instead; the guard is the least invasive.)*

------

### 5) (Optional but nice) `src/cpp/Region.cpp` — de‑warning for misleading indentation

```diff
--- a/src/cpp/Region.cpp
+++ b/src/cpp/Region.cpp
@@
-    auto isAllZero = [](const std::vector<std::vector<double>>& img) {
-        for (const auto& row : img) for (double v : row) if (v != 0.0) return false; return true;
-    };
+    auto isAllZero = [](const std::vector<std::vector<double>>& img) {
+        for (const auto& row : img) {
+            for (double v : row) {
+                if (v != 0.0) return false;
+            }
+        }
+        return true;
+    };
@@
-                    if (v > 0.0) {
-                        ++active; total += v; sumR += r * v; sumC += c * v;
-                        if (r < rmin) rmin = r; if (r > rmax) rmax = r;
-                        if (c < cmin) cmin = c; if (c > cmax) cmax = c;
-                    }
+                    if (v > 0.0) {
+                        ++active;
+                        total += v;
+                        sumR  += r * v;
+                        sumC  += c * v;
+                        if (r < rmin) rmin = r;
+                        if (r > rmax) rmax = r;
+                        if (c < cmin) cmin = c;
+                        if (c > cmax) cmax = c;
+                    }
```

------

## Why this fixes your errors

- The **“invalid use of incomplete type ‘Neuron’”** errors all come from *using* a forward‑declared type’s **members** in a header. After this PR, the header no longer touches `Neuron`; the implementation file does (and includes `Neuron.h`), so the type is complete there.
- The **`Region::prune` not found** errors were from demo sources that still call a method your current C++ class does not implement. Guarding/removing those calls lets the project build without forcing API work you didn’t plan.
- The **misleading‑indentation warnings** are cosmetic; fixing them keeps CLion/MinGW from drowning you in noise.

