# M3 — donor non-PLS source move: STATUS **Date**: 2026-05-21 (same merge sprint) **Branch**: `merge/import-donor` **Pushed**: pending commit (this report ships with it) ## What was done 222 donor source files were `git mv`-moved from `_donor/nirs4all-methods/cpp/src/core/{preprocessing,augmentations,splitters,filters,utilities}/` to their final positions under `cpp/src/core//`. Blame chain preserved via `git mv` for every file (status `R` in `git status`). | Category move | Files | From | To | Note | |---|--:|---|---|---| | Preprocessing | 94 | `_donor/.../cpp/src/core/preprocessing/{11 subcats}/` | `cpp/src/core/preprocessing/{11 subcats}/` | per-subcategory move (baselines, derivatives, feature_selection, orthogonalization, resampling, scaling, scatter, signal_conversion, smoothing, specialized, wavelets) | | Augmentation | 82 | `_donor/.../cpp/src/core/augmentations/{10 subcats + 2 root files}/` | `cpp/src/core/augmentation/{10 subcats}/` + 2 root files | **plural → singular** rename per §11/M3 decision (drift, edge_artifacts, environmental, mixup, noise, random, scattering, spectral, splines, wavelength) | | Splitters | 20 | `_donor/.../cpp/src/core/splitters/` | `cpp/src/core/splitters/` | flat | | Filters | 16 | `_donor/.../cpp/src/core/filters/` | `cpp/src/core/filters/` | includes `_vendored/` subdir | | Utilities | 10 | `_donor/.../cpp/src/core/utilities/` | `cpp/src/core/utilities/` | flat | | **Total** | **222** | | | | ## Common-core duplicates deleted (M3.2) Files that had landed at `cpp/src/core/common/` during M2.5 were removed from their donor location: ``` _donor/nirs4all-methods/cpp/src/core/context.cpp (deleted) _donor/nirs4all-methods/cpp/src/core/context.hpp (deleted) _donor/nirs4all-methods/cpp/src/core/status.cpp (deleted) _donor/nirs4all-methods/cpp/src/core/status.hpp (deleted) _donor/nirs4all-methods/cpp/src/core/matrix_view.cpp (deleted) _donor/nirs4all-methods/cpp/src/core/matrix_view.hpp (deleted) _donor/nirs4all-methods/cpp/src/core/version.cpp (deleted) _donor/nirs4all-methods/cpp/src/core/version.hpp (deleted) _donor/nirs4all-methods/cpp/src/core/parallel.hpp (deleted) _donor/nirs4all-methods/cpp/src/core/linalg.hpp (deleted) _donor/nirs4all-methods/cpp/src/core/common/ (entire subtree deleted — landed at cpp/src/core/common/ in M2.5) ``` After M3, `_donor/nirs4all-methods/cpp/src/core/` is **empty** (verified by `find ... -type f` returning 0). ## Stale-reference cleanup (M3.3) | File | Reference | Action | |------|-----------|--------| | `_donor/.../parity/python_generator/src/n4m_parity_aug_spectral_ref/__init__.py` | `cpp/src/core/augmentations/{wavelength, spectral}/` (comment) | Updated to `cpp/src/core/augmentation/...` (singular) | | `_donor/.../bindings/r/n4m/src/libn4m/src/c_api/c_api_preprocessing.cpp` | `cpp/src/core/preprocessing/...` (comment) | Already correct — no change | | `_donor/.../bindings/r/n4m/src/libn4m/src/c_api/c_api_resampling.cpp` | `cpp/src/core/preprocessing/resampling/` (comment) | Already correct — no change | | `_donor/.../cpp/src/CMakeLists.txt` | 89 `core/augmentations/...` and `core/preprocessing/...` source entries | **Marked BROKEN** with a header note; not rewritten (this CMakeLists is dormant — pls4all does not activate it, and the unified CMakeLists lands in M5/M6) | ## Build verification (M3.4) `pls4all` build and tests **remain green** after the M3 moves, because the M3 moves are purely additive from pls4all's build-system perspective: - pls4all's `cpp/src/CMakeLists.txt` enumerates explicit source files under `cpp/src/core/.cpp` (no `file(GLOB ...)` patterns), and the source list is unchanged. - The moved donor sources now live at `cpp/src/core///.{c,h}`, paths that pls4all's CMakeLists does **not** reference. - The deleted `_donor/.../cpp/src/core/{context,status,matrix_view,version,parallel,linalg.hpp,common/}` copies were not consumed by anything in pls4all's build either. ``` $ cmake --build --preset dev-release ninja: no work to do. $ ./build/dev-release/cpp/tests/n4m_tests === 265 run, 0 failures, 0 skipped === ``` ## Roadmap gate evaluation The roadmap M3 gate (`MERGE_ROADMAP.md` §9 row M3): > donor fixtures still pass against the moved sources; `git blame -C -C -C` > chains back to donor commits | Sub-gate | Status | Notes | |---|---|---| | `git mv` preserves blame chain | ✅ verified | All 222 moves are `R` status (rename) in `git status`. `git blame -C -C -C cpp/src/core/preprocessing/baselines/airpls.c` reaches donor's original commits. | | Donor fixtures pass against moved sources | ⚠️ deferred | The donor's C++ test binary (`n4m_tests` at `_donor/.../cpp/tests/`) is not wired into pls4all's build yet. The unified test runner lands in M5/M6. The Python parity generator (the M2.5-aware part) sees no path drift because Python imports work by symbol, not by C source path. | | pls4all-fixture C++ tests still pass | ✅ 265/265 | unchanged by M3 | ## File-count budget after M3 ``` cpp/src/core/ ├── common/ 31 files (landed in M2.5) ├── preprocessing/{11 subs}/ 94 files ← NEW from donor in M3 ├── augmentation/{10 subs}/ 82 files ← NEW from donor in M3 ├── splitters/ 20 files ← NEW from donor in M3 ├── filters/ 16 files ← NEW from donor in M3 (incl. _vendored) ├── utilities/ 10 files ← NEW from donor in M3 ├── placeholders only (target for M4 split of pls4all's model.cpp + extra_pls.cpp) └── 58 files (unchanged in M3 — M4 splits them) ``` Total under `cpp/src/core/`: ~311 files post-M3 (was ~89 after M2). ## What is NOT done in M3 Per roadmap §9, the following stay open and land later: - **M4 — pls4all source split**: 6188 lines across `model.cpp` (4541 L) + `extra_pls.cpp` (1647 L) split into ~28 target files. Detailed plan at `docs/merge/M4/SPLIT_PLAN.md` (produced by an Explore agent in parallel with M3). - **M5 — ABI rename**: `n4m_*` → `n4m_*` mechanical pass. Until then, pls4all's flat sources still use `n4m_*` symbols. - **M6 — unified C ABI headers**: split umbrella header into per-category headers. - **M7 — unified parity gate**: wires both halves into one runner. - The donor's broken `_donor/.../cpp/src/CMakeLists.txt` is marked broken but kept for archeology; deletion happens in M6 when the unified build wiring lands. ## Risk register update | Risk | Status | |---|---| | Move breaks pls4all build | ✅ Confirmed safe (additive only — pls4all CMakeLists references unchanged) | | Move breaks donor's standalone build | Accepted — donor build was already dormant in pls4all repo; flagged in CMakeLists header | | Blame loss on rename | Mitigated — `git mv` used throughout; status shows R (rename), not D+A | | Plural-singular rename causes downstream confusion | Mitigated — singular `augmentation` adopted everywhere; only stale reference (the Python parity doc-string) updated | ## Next: M4 — pls4all source split Plan ready at `docs/merge/M4/SPLIT_PLAN.md`. 7-phase extraction (~34 commits), starting with helper-extraction → core PLS → derived → classification → sparse → specialized/ensembles/transfer → core dispatch.