## PR — No‑op prune stubs (keep demos compiling) **[no RcBin]**

### Summary

- Add a tiny `PruneSummary` struct and **no‑op** `Region::prune(...)` overloads in C++ so the existing demos/tests that call `prune(...)` compile and run unchanged.
- **No functional changes** to learning/propagation.
- **No RcBin**: nothing about slot keys changes. (Optional revert snippets at the end remove RcBin if it already landed locally.)

### Acceptance

- `cmake --build . -j` completes.
- C++ demos that call `region.prune(...)` link and run (stub returns zeros).
- Unit tests still green.

------

### Patches

#### 1) `src/cpp/Region.h` — add `PruneSummary` + method declarations

```diff
--- a/src/cpp/Region.h
+++ b/src/cpp/Region.h
@@
 namespace grownet {
 
+// Minimal summary for prune operations (no-op for now).
+struct PruneSummary {
+    long long prunedSynapses {0};
+    long long prunedEdges    {0};  // reserved for future use
+};
+
 class Region {
 public:
@@
+    // --- No-op prune stubs to keep demos/tests compiling ---
+    PruneSummary prune(long long synapseStaleWindow, double synapseMinStrength);
+    PruneSummary prune(long long synapseStaleWindow, double synapseMinStrength,
+                       long long tractStaleWindow,  double tractMinStrength);
```

#### 2) `src/cpp/Region.cpp` — implement the no‑op stubs

```diff
--- a/src/cpp/Region.cpp
+++ b/src/cpp/Region.cpp
@@
 namespace grownet {
@@
+PruneSummary Region::prune(long long /*synapseStaleWindow*/, double /*synapseMinStrength*/) {
+    // No-op: real pruning will come later; this unblocks demos/tests that invoke prune().
+    return PruneSummary{};
+}
+
+PruneSummary Region::prune(long long /*synapseStaleWindow*/, double /*synapseMinStrength*/,
+                           long long /*tractStaleWindow*/,  double /*tractMinStrength*/) {
+    // No-op variant with tract arguments.
+    return PruneSummary{};
+}
```

> That’s it for the requested change. Nothing else is required.

------

## (Only if needed) Optional cleanup to **remove RcBin** if it already landed

If you **already** applied any RcBin / `slots2D` edits locally, use the snippets below to revert back to the original integer‑packed key (`rb * 100000 + cb`) and a single `slots` map.

> If your tree does **not** contain RcBin, skip this section.

#### A) `src/cpp/Neuron.h` — drop RcBin & `slots2D` (if present)

```diff
--- a/src/cpp/Neuron.h
+++ b/src/cpp/Neuron.h
@@
-// 2-D slot key (row_bin, col_bin)
-struct RcBin {
-    int r; int c;
-    bool operator==(const RcBin& o) const noexcept { return r==o.r && c==o.c; }
-};
-struct RcBinHash {
-    std::size_t operator()(const RcBin& k) const noexcept { /* ... */ }
-};
@@
-    // 2-D spatial slots (Phase B)
-    std::unordered_map<RcBin, Weight, RcBinHash> slots2D;
@@
-    std::unordered_map<RcBin, Weight, RcBinHash>& getSlots2D()       { return slots2D; }
-    const std::unordered_map<RcBin, Weight, RcBinHash>& getSlots2D() const { return slots2D; }
```

#### B) `src/cpp/SlotEngine.cpp` — use the original integer‑packed key for 2‑D

```diff
--- a/src/cpp/SlotEngine.cpp
+++ b/src/cpp/SlotEngine.cpp
@@
-    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);
-    RcBin key{ rb, cb };
-
-    auto& slots2d = neuron.getSlots2D();
-    auto  it      = slots2d.find(key);
-    if (it == slots2d.end()) it = slots2d.emplace(key, Weight{}).first;
-    return it->second;
+    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, fast packing (kept intentionally)
+
+    auto& slots = neuron.getSlots();
+    auto  it    = slots.find(key);
+    if (it == slots.end()) it = slots.emplace(key, Weight{}).first;
+    return it->second;
```

#### C) `src/cpp/Region.cpp` — metrics: count only `slots` (remove `getSlots2D()` if present)

```diff
--- a/src/cpp/Region.cpp
+++ b/src/cpp/Region.cpp
@@
-            metrics.totalSlots    += static_cast<long long>(neuron->getSlots().size());
-            metrics.totalSlots    += static_cast<long long>(neuron->getSlots2D().size());
+            metrics.totalSlots    += static_cast<long long>(neuron->getSlots().size());
```

------

## Build & quick sanity

```bash
cmake --build cmake-build-debug -j
# if you added the guarded smoke test earlier:
# g++ -std=c++17 -DGROWNET_WINDOWED_WIRING_SMOKE -Isrc/cpp src/cpp/*.cpp src/cpp/tests/WindowedWiringSmoke.cpp -o win_smoke
# ./win_smoke
```

Everything above keeps the codebase behavior identical, just restores your demos/tests by providing a harmless `Region::prune` stub.