Skip to content

Slice 12 — rbfmaxTrainAndSave command + v1.1.0 (Phase 2A close-out) - #10

Merged
891458249 merged 5 commits into
mainfrom
slice-12-train-command
Apr 21, 2026
Merged

Slice 12 — rbfmaxTrainAndSave command + v1.1.0 (Phase 2A close-out)#10
891458249 merged 5 commits into
mainfrom
slice-12-train-command

Conversation

@891458249

Copy link
Copy Markdown
Owner

Summary

Phase 2A close-out slice. Adds the rbfmaxTrainAndSave MPxCommand 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.jsonPath to serve predict() in the DG.

Commits (5)

  1. feat(maya) d3bba17 — adapter_core training utilities + 8 new TEST blocks (D group)
  2. feat(maya) 53ff9d7rbfmaxTrainAndSave MPxCommand + 4-scenario smoke + CSV fixtures
  3. build(cmake) 7e0de3b — project VERSION 1.0.0 → 1.1.0
  4. docs(readme) 368e507 — README / CHANGELOG / maya_node/README Phase 2A complete
  5. docs(devlog) 24c6c5f — Slice 12 + Phase 2A retrospective

10 locked design decisions (detailed in DEVLOG)

  • Command-based training (not node-based) — keeps the Slice 11 JSON-path architecture intact
  • Two mutually exclusive modes: inline (Python lists) and CSV (pipeline files)
  • 12 flags, one required (-jsonPath), seven shared, four mode-specific
  • Not undoable (writes a file); -force required to overwrite existing
  • MS::kFailure + displayError → Python RuntimeError
  • Multi-use kDouble flag idiom for inline doubleArray inputs
  • CSV format: #-comment tolerated, blank lines skipped, uniform column count enforced
  • Bit-identity smoke against Slice 11 reference table

R-09 / grep catches & F-fixes

  • Grep-verified all Phase 1 references before writing any code.
  • F5 (new, this slice) — Maya MSyntax::addFlag silently rejects long flag names shorter than 4 characters. Spec's -dim / -eps were being dropped at registration time, surfacing in Python as TypeError: Invalid flag 'eps'. Tracked via a diagnostic wrapper around addFlag logging kFailure returns, then confirmed by cmds.help() listing 10/12 flags. Renamed to -inputDim / -epsilon and everything works. Registered as R-30 in the tech-debt cookbook.

Local verification (Windows 11, MSVC 19.44)

Step Command Result
1 Adapter + Phase 1 ctest 154/154 green, 12.91 s (137 Phase 1 + 3 H + 6 C + 8 D)
2a Maya 2022 .mll 0 warn 0 err, 501 760 bytes
2b Maya 2025 .mll 0 warn 0 err, 501 760 bytes (byte-identical)
3a Maya 2022 smokes × 3 (hellonode, predict, train) all exit 0, bit-identical to expected
3b Maya 2025 smokes × 3 all exit 0, bit-identical to 2022
4 Phase 1 Release regression 137/137 green, 11.93 s

Phase 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)

  1. git checkout main && git pull
  2. git tag v1.1.0 <merge-sha> + git push origin v1.1.0
  3. gh 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)
  4. CI should auto-trigger benchmark-smoke on the tag push (same pattern as v1.0.0).

Test plan

  • Local adapter + Phase 1: 154/154
  • Local Maya 2022 + 2025 builds: 0 warn 0 err, byte-identical
  • Local Maya 2022 smokes × 3: all exit 0
  • Local Maya 2025 smokes × 3: all exit 0 bit-identical
  • Local Phase 1 regression: 137/137
  • CI Windows MSVC Release
  • CI Windows MSVC Debug
  • CI Ubuntu GCC 11 Release

Version

v1.0.0 → v1.1.0 (MINOR, additive — Maya plugin + training command; no Phase 1 breaking changes)

🤖 Generated with Claude Code

d891458249-rgb and others added 5 commits April 21, 2026 17:00
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>
@891458249
891458249 merged commit 7296abf into main Apr 21, 2026
4 checks passed
@891458249
891458249 deleted the slice-12-train-command branch April 21, 2026 09:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants