M5 — ABI rename n4m_* → n4m_*: STATUS¶
Status: 🟡 SCRIPT READY, EXECUTION DEFERRED
Date: 2026-05-21 (same merge sprint)
Pre-rename tag: abi-p4a-final/v0.97.3-pre-merge → 9472dd9 (pushed)
What was done in this commit¶
Script written at
scripts/migrate_p4a_to_n4m.py— a stdlib-only Python tool that performs the mechanical rename across selectable stages (core,bindings,parity,benchmarks,docs,all) with a--dry-runmode for impact analysis.Dry-run impact analysis completed:
Stage |
Files affected |
Substitutions |
|---|---|---|
|
293 |
14083 |
|
~150+ |
~16000 |
|
~hundreds |
~17600 |
Total |
~600 files |
~18500 token replacements |
Pre-rename tag landed:
abi-p4a-final/v0.97.3-pre-merge(pushed to origin) so the historicaln4m_*symbol surface stays recoverable.
What is NOT done (why M5 is deferred)¶
A live attempt at executing --stage core exposed a deeper-than-mechanical
issue with the public-header layout that the M5 rename alone cannot solve:
pls4all’s renamed
p4a.h(now declaringn4m_*symbols) lives atcpp/include/pls4all/p4a.hand contains the PLS-side ABI surface.Donor’s
n4m.hatcpp/include/n4m/n4m.his the umbrella header declaring the non-PLS surface (preprocessing, augmentation, splitter, filter, utility).After
pls4all/p4a.h → n4m/n4m.hsubstitution in pls4all source includes, those sources end up including the donor’sn4m.h— which does NOT declare the PLS symbols they need. Build fails with unresolvedn4m_simpls_fitetc.
The fix is not a simple sed: it needs the donor’s n4m.h and pls4all’s
renamed p4a.h content to be merged into one umbrella header without
duplicating the common-core declarations (context, status, matrix_view,
backend codes — present in both). That merge is M6’s job
(per MERGE_ROADMAP.md row M6: “split into per-category headers”).
So the correct execution sequence is M5 ∘ M6: the rename runs WHILE the header layout is being collapsed; doing M5 alone strands the build in an inconsistent state.
Plan to actually execute M5 (in a focused session paired with M6)¶
First, run M6 header reorganisation:
Move pls4all’s
cpp/include/pls4all/p4a.hcontent into split files atcpp/include/n4m/{n4m.h umbrella, n4m_pls.h, n4m_selection.h, …}.Strip duplicate common-core declarations.
The donor’s existing
cpp/include/n4m/n4m.hbecomes the umbrella that#includes the new per-category headers.
Then run M5 (script):
python scripts/migrate_p4a_to_n4m.py --apply --stage core # Build + test gate cmake --build --preset dev-release && \ ./build/dev-release/cpp/tests/n4m_tests # Should now report 265/265 with n4m_* symbols
Then run M5 on the binding stages, one at a time, gated by the binding’s own smoke tests:
python scripts/migrate_p4a_to_n4m.py --apply --stage bindings # 16K subs python scripts/migrate_p4a_to_n4m.py --apply --stage parity python scripts/migrate_p4a_to_n4m.py --apply --stage benchmarks python scripts/migrate_p4a_to_n4m.py --apply --stage docs
Symbol-table normalisation (Linux versioned ELF
@@N4M_1):Update
cpp/abi/expected_symbols_linux.txtto versioned-symbol format.Update visibility script (
pls4all.ld→n4m.ld, the version-script that grants visibility).Adjust macOS
-exported_symbols_listand Windows.defif needed.
Why deferred to a focused session¶
~18500 token replacements across ~600 files: a single failed intermediate state breaks 265 unit tests, all bindings, all parity fixtures, and the dashboard. The rollback surface is the entire repo.
Header layout dependency: M5 cannot land cleanly without the M6 header split (see above).
Per-binding test gates: each binding (Python/R/MATLAB/JS/Julia/Go/Rust/.NET/Ruby/Lua/Nim/JNI) has its own smoke test that needs to run after the rename. The session driver has to manually exercise each binding’s local test runner.
Recovery commands if a future attempt corrupts the tree¶
# Hard reset to the pre-rename state:
git reset --hard abi-p4a-final/v0.97.3-pre-merge
# Or rebuild from M4 scaffold:
git checkout 9472dd9 -- cpp/ CMakeLists.txt
Caveats noted by Codex on M4 review (carried into M5 planning)¶
M4/EXTRACTION_RECIPE.md doesn’t yet make “M4 partial” explicit at the top — should be updated to reflect that the actual extraction lands AFTER M5 rename so the new files start life with
n4m_*naming, notn4m_*.SPLIT_PLAN.mdsaysapproximate_press → dispatch.cppwhile we placed it atmodels/core/approximate_press.cpp— should reconcile.After M5 rename, M4 phase-1 helper extractions need to use
n4m::coreandn4m::detailnamespaces, notn4m::core.
These caveats are written into the M5 plan above; they apply to the focused session that executes both M5 and M6.
What this commit lands¶
File |
Purpose |
|---|---|
|
Stdlib-only mechanical rename script (–dry-run + –apply with per-stage selection). Ready to run after M6 header split lands. |
|
This status report (off-repo working notes). |
Tag |
Historical pre-rename baseline, lets future attempts roll back cleanly. |