Slice 12 — rbfmaxTrainAndSave command + v1.1.0 (Phase 2A close-out) - #10
Merged
Conversation
Phase 2A Slice 12 piece 1 of 5. Extends the adapter_core.hpp layer
with four utilities that back the upcoming rbfmaxTrainAndSave
MPxCommand. Kept pure-C++ + Maya-free so the GTest adapter suite
can cover them without the devkit.
Utilities (all C++14-compliant for the dual-standard constraint):
* unflatten_double_array(flat, D, out) — inline-mode helper;
reshapes a row-major double vector into an N x D MatrixX.
Returns false with out untouched on non-positive D or shape
mismatch (atomic-update contract, mirrors Phase 1 load semantics).
* file_exists(path) — alias-spelling of the
Slice 11 validate_json_path helper, used by the command for
--force overwrite gating.
* parse_csv_matrix(path, out, err) — csv-mode helper;
non-noexcept (std::stod can throw); '#' line comments and blank
lines skipped; enforces uniform column count; err_reason carries
a short human-readable diagnostic on failure.
* parse_lambda_arg(s, is_auto, value) — maps "auto"/"AUTO"/"Auto"
to is_auto=true, otherwise uses std::stod end-to-end (rejects
trailing garbage). Non-noexcept for the same reason.
The non-inline pair lives in maya_node/src/adapter_core_csv.cpp — a
new TU linked into BOTH the plugin target (via maya_node/CMakeLists)
AND the adapter test target (via maya_node/tests/CMakeLists.txt).
Keeping it out of the header shields the plugin's no-throw
compute path from std::string-related exception specifications.
Tests — 8 new TEST blocks in the D group:
D1 UnflattenSimple — {1..6}, D=2 -> [[1,2],[3,4],[5,6]]
D2 UnflattenBadLength — length not a multiple of D -> false;
out must be untouched
D3 UnflattenBadDim — D=0 and D=-1 both reject
D4 ParseCsvSimple — two-row CSV parses exactly
D5 ParseCsvWithComments — '#' comments + blank lines skipped
D6 ParseCsvColMismatch — ragged rows produce err with
"mismatch" substring
D7 ParseLambdaAuto — three case variants all resolve
to is_auto=true
D8 ParseLambdaNumeric — numeric + trailing-garbage rejection
+ file_exists spot-check
Random seed kSeedS12 = 0xF5BFABu reserved for future randomised
additions; anchored via the H3 EXPECT_NE(kSeedS12, 0u) marker so
-Wunused-variable stays quiet under /WX.
Local verification (Windows 11, MSVC 19.44 Release):
Step 1 adapter + Phase 1 regression: 154/154 green, 12.91 s
(137 Phase 1 + 3 H + 6 C + 8 D)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phase 2A Slice 12 piece 2 of 5. Adds the MPxCommand that closes the
Slice 11 "trainers cannot train from inside Maya" gap. Two mutually
exclusive input modes:
1. Inline mode (flag-driven double lists):
cmds.rbfmaxTrainAndSave(
centers=[x0, y0, x1, y1, ...],
targets=[t0, t1, ...],
inputDim=2, outputDim=1,
jsonPath="...", kernel="Gaussian", epsilon=1.0,
polyDegree=-1, force=True, **{"lambda":"1e-6"})
2. CSV mode (path-driven, the scalable option):
cmds.rbfmaxTrainAndSave(
centersFile="centers.csv", targetsFile="targets.csv",
jsonPath="...", kernel="Gaussian", epsilon=1.0, ...)
Both paths call Phase 1 solver::fit under the hood, then
RBFInterpolator::save to produce a schema-v1 JSON that the Slice 11
mRBFNode can load. On success returns the jsonPath string so callers
can chain into setAttr (setAttr <node>.jsonPath `rbfmaxTrainAndSave
...`).
12 flags, all validated to register in both Maya 2022 and Maya 2025.
------------------------------------------------------------------
F5 (Slice 12 executor catch) — long flag names must be >= 4 chars
------------------------------------------------------------------
Maya's MSyntax::addFlag silently returns kFailure "Unexpected
Internal Failure" for long flag names shorter than 4 characters.
First drafts used spec's literal -dim / -eps; both were silently
dropped, manifesting at Python bind time as `TypeError: Invalid
flag 'eps'`. Discovered via a diagnostic wrapper around addFlag
that surfaced the kFailure return, then confirmed by help() listing
10 of 12 flags registered.
Renamed to -inputDim and -epsilon (and Python keyword args match).
Rule documented in the MSyntax comment block and in DEVLOG R-30.
Other MSyntax findings from the same debugging:
* MArgDatabase::getFlagArgumentList(flag, i, MArgList&) is the
correct way to read each use of a multi-use kDouble flag —
getFlagArgument(flag, i, double&) exists but reads the i-th
*flag's* first argument, not what we want. Multi-use pattern
wraps one double per flag-use into an MArgList which the reader
extracts index 0 from.
Plugin_main.cpp:
* Added RbfmaxTrainCmd registerCommand + deregisterCommand wiring.
* initializePlugin: registerNode -> registerCommand with a
best-effort deregisterNode rollback if the command registration
fails (keeps loadPlugin atomic).
* uninitializePlugin: deregisterCommand first, then deregisterNode;
both errors are surfaced but unwinding continues.
Smoke coverage (smoke_train.py, 4 scenarios):
S1 CSV-mode train + mRBFNode load + predict bit-identical to
Slice 11 tiny_rbf_expected.json (all 3 queries err < 1e-10)
S2 Inline-mode train, same bit-identity check
S3 --force=False against existing file raises RuntimeError
with "file exists" substring
S4 kernel="Nonsense" raises RuntimeError with "unknown kernel"
or "nonsense" substring
Fixtures (committed):
maya_node/tests/smoke/fixtures/tiny_train_centers.csv (4x2)
maya_node/tests/smoke/fixtures/tiny_train_targets.csv (4x1)
Both mirror the Slice 11 tiny_rbf.json data so the train-then-predict
bit-identity assertion is meaningful.
Local verification (Windows 11, MSVC 19.44 Release):
Step 2a Maya 2022 plugin: 501 760 bytes, 0 warn 0 err
Step 2b Maya 2025 plugin: 501 760 bytes, 0 warn 0 err (byte-
identical, matches the Slice 11
version-matrix ABI-agnosticism)
Step 3a Maya 2022 smokes: hellonode/predict/train all exit 0
Step 3b Maya 2025 smokes: all exit 0, bit-identical to 2022
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phase 2A Slice 12 piece 3 of 5. Advances the project SemVer to
1.1.0 to mark Phase 2A as complete. No tag in this commit —
tagging is the human-channel step after the PR merges onto main.
Changes:
* top-level CMakeLists.txt project(... VERSION 1.1.0 ...)
This also updates RBFMAX_VERSION_{MAJOR,MINOR,PATCH} compile
definitions and the io_json meta.version string at save time.
* maya_node/CMakeLists.txt: RBFMAX_MAYA_PLUGIN_VERSION now plain
"1.1.0" (dropped the -phase2a-sliceNN suffix since Phase 2A is
finished; future slices will reintroduce a suffix if needed).
SemVer accounting (from v1.0.0 baseline):
ADDED (MINOR bump):
* maya_node/ plugin (mRBFNode + rbfmaxTrainAndSave)
* RBFInterpolator::kernel_params() public getter
* adapter_core utilities (Slice 11 + 12)
BREAKING: none
DEPRECATIONS: none
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phase 2A Slice 12 piece 4 of 5. User-facing documentation update
for the v1.1.0 release.
Top-level README.md:
* Badge bumped 1.0.0 -> 1.1.0 with the corresponding release-tag
link (the tag itself is still pending — the human-channel step
after PR merge).
* Status section rewritten: "Phase 2A complete (v1.1.0)" with a
one-paragraph summary of what the Maya plugin now does (mRBFNode
+ rbfmaxTrainAndSave, double-validated on Maya 2022 / 2025),
and forward-looks to Phase 2B (Viewport 2.0 draw override) +
Phase 2C (Qt6 UI).
* Features list appended: "Maya 2022/2025 plugin" bullet, points
at maya_node/README.md.
* Roadmap restructured: Phase 2A ticked, 2B and 2C subdivided,
Phase 3/4 unchanged.
CHANGELOG.md:
* New [1.1.0] entry covering every user-visible Phase 2A delivery:
the .mll / .so plugin, mRBFNode attributes, rbfmaxTrainAndSave
command flags and modes, kernel_params() getter, FindMaya cmake
module, mayapy smoke suite, Maya version matrix status.
* "Known limitations" enumerates the real caveats a consumer
should see (dev typeId, Maya 2024/2026 deferred, not undoable,
MSyntax >= 4 char long names).
* Build subsection documents the two opt-in CMake flags.
* v1.0.0 entry untouched (append-only).
maya_node/README.md:
* New "Command: rbfmaxTrainAndSave (Slice 12)" section between
Usage and Known limitations. 12-flag table, inline example,
CSV example with the fixture contents inline, typical 3-step
workflow, exhaustive error-message catalogue.
* Files table rewritten to reflect the actual Slice 12 layout:
all 17 adapter TESTs, 3 smoke scripts, 4 fixtures (2 JSON + 2
CSV), every source file including the new TUs.
* "Known limitations" updated: dynamic array attributes no longer
a TODO (Slice 11 delivered them), Maya 2025 validation done
(Slice 10C), remaining gap is 2024 / 2026 devkits.
No DEVLOG change in this commit — that lands as the next commit
along with the Phase 2A retrospective.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phase 2A Slice 12 piece 5 of 5, and the Phase 2A close-out artifact.
Slice 12 entry covers:
* Scope — close the Slice 11 "must train externally" gap with
a new MPxCommand; bump SemVer to 1.1.0.
* Full deliverables list across adapter_core extensions, CSV
parser TU, command header+impl, plugin registration, CMake
wiring, 8 new adapter tests, smoke with 4 scenarios, 2 CSV
fixtures, version bump, README / CHANGELOG / maya_node/README
updates.
* 10 locked design decisions — command vs node, two-mode
mutual exclusion, 12 flag matrix, non-undoable trade-off,
error-surface contract, multi-use doubleArray idiom, CSV
format spec, bit-identity smoke assertion.
* F5 deep-dive — MSyntax::addFlag silently rejects long names
shorter than 4 chars. Full debugging trail: first drafts
using -dim/-eps, short-name rename attempts, diagnostic
instrumentation, eventual confirmation via help() listing,
resolution to -inputDim/-epsilon. Registered as R-30.
* Validation table — Step 1 adapter+Phase 1 154/154, Step 2a/2b
Maya 2022/2025 builds 0 warn/err at byte-identical 501 760 B,
Step 3a/3b six smokes all exit 0 with bit-identical output
across versions, Step 4 Phase 1 regression 137/137.
Phase 2A Retrospective:
* Timeline: v1.0.0 -> v1.1.0 in one calendar-compressed day.
* 4 slices shipped (10A / 10C / 11 / 12); 10B + 10D deferred
non-blocking on devkit availability.
* Code inventory: maya_node ~1.9k LOC, tests ~760 LOC, 4
fixtures, 2 CMake modules, 1 Phase 1 additive amendment.
Plugin binary: 25 KB (10A) -> 502 KB (12).
* What went well: grep-verify protocol, JSON-path architecture,
double-environment smoke, additive Phase 1 amendment pattern.
* What to improve in Phase 2B: flag-name grep discipline,
generator-based fixture reproducibility, budgeting for Maya
API shakeout, version-matrix acceleration.
* Tech debt: R-29 / R-30 living cookbook items; T-10 Autodesk
typeId block registration; T-11 closed; T-12 v1.1.0 GitHub
Release (closes at human-channel tag push); 10B / 10D
version-matrix open but non-blocking.
* Entry conditions for Phase 2B documented; context handoff
instructions restated (required reading: DEVLOG 10A onward,
maya_node/README, CHANGELOG [1.1.0]).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Phase 2A close-out slice. Adds the
rbfmaxTrainAndSaveMPxCommand that closes Slice 11's "must train externally" gap, and bumps the project to v1.1.0 to mark Phase 2A complete.With this merged: users can now train a Phase 1 RBF interpolator from inside Maya (two input modes), save schema-v1 JSON, and immediately load it through
mRBFNode.jsonPathto servepredict()in the DG.Commits (5)
feat(maya)d3bba17— adapter_core training utilities + 8 new TEST blocks (D group)feat(maya)53ff9d7—rbfmaxTrainAndSaveMPxCommand + 4-scenario smoke + CSV fixturesbuild(cmake)7e0de3b— project VERSION 1.0.0 → 1.1.0docs(readme)368e507— README / CHANGELOG / maya_node/README Phase 2A completedocs(devlog)24c6c5f— Slice 12 + Phase 2A retrospective10 locked design decisions (detailed in DEVLOG)
-jsonPath), seven shared, four mode-specific-forcerequired to overwrite existingMS::kFailure+displayError→ PythonRuntimeErrorkDoubleflag idiom for inline doubleArray inputs#-comment tolerated, blank lines skipped, uniform column count enforcedR-09 / grep catches & F-fixes
MSyntax::addFlagsilently rejects long flag names shorter than 4 characters. Spec's-dim/-epswere being dropped at registration time, surfacing in Python asTypeError: Invalid flag 'eps'. Tracked via a diagnostic wrapper aroundaddFlaglogging kFailure returns, then confirmed bycmds.help()listing 10/12 flags. Renamed to-inputDim/-epsilonand everything works. Registered as R-30 in the tech-debt cookbook.Local verification (Windows 11, MSVC 19.44)
ctest.mll.mllPhase 2A Retrospective (in DEVLOG)
4 slices shipped (10A / 10C / 11 / 12). v1.0.0 → v1.1.0 in one calendar-compressed day. Plugin binary 25 KB → 502 KB as functionality layered in. Double-environment smoke bit-identity holds across every slice. 10B (Maya 2024) and 10D (Maya 2026) deferred non-blocking; architecture proven version-agnostic.
Post-merge steps (human channel)
git checkout main && git pullgit tag v1.1.0 <merge-sha>+git push origin v1.1.0gh release create v1.1.0 --title "v1.1.0 — Phase 2A complete" --notes-file <(awk '/## \[1.1.0\]/,/## \[1.0.0\]/' CHANGELOG.md | head -n -1)benchmark-smokeon the tag push (same pattern as v1.0.0).Test plan
Version
v1.0.0 → v1.1.0 (MINOR, additive — Maya plugin + training command; no Phase 1 breaking changes)
🤖 Generated with Claude Code